rumdl 0.0.74__tar.gz → 0.0.76__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 (266) hide show
  1. {rumdl-0.0.74 → rumdl-0.0.76}/Cargo.lock +1 -1
  2. {rumdl-0.0.74 → rumdl-0.0.76}/Cargo.toml +1 -1
  3. {rumdl-0.0.74 → rumdl-0.0.76}/PKG-INFO +1 -1
  4. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md005_list_indent.rs +6 -0
  5. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md006_start_bullets.rs +14 -13
  6. rumdl-0.0.76/src/rules/md011_no_reversed_links.rs +453 -0
  7. rumdl-0.0.76/src/rules/md018_no_missing_space_atx.rs +479 -0
  8. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md019_no_multiple_space_atx.rs +8 -5
  9. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md021_no_multiple_space_closed_atx.rs +12 -34
  10. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md023_heading_start_left.rs +16 -20
  11. rumdl-0.0.76/src/rules/md027_multiple_spaces_blockquote.rs +269 -0
  12. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md028_no_blanks_blockquote.rs +6 -0
  13. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md030_list_marker_space.rs +13 -25
  14. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md031_blanks_around_fences.rs +6 -0
  15. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md032_blanks_around_lists.rs +2 -2
  16. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md037_spaces_around_emphasis.rs +6 -0
  17. {rumdl-0.0.74 → rumdl-0.0.76}/tests/character_ranges/basic_tests.rs +1 -1
  18. {rumdl-0.0.74 → rumdl-0.0.76}/tests/character_ranges/extended_tests.rs +2 -2
  19. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md019_test.rs +1 -1
  20. rumdl-0.0.76/tests/vscode_extension_fixes.rs +375 -0
  21. rumdl-0.0.74/src/rules/md011_no_reversed_links.rs +0 -233
  22. rumdl-0.0.74/src/rules/md018_no_missing_space_atx.rs +0 -257
  23. rumdl-0.0.74/src/rules/md027_multiple_spaces_blockquote.rs +0 -111
  24. {rumdl-0.0.74 → rumdl-0.0.76}/.rumdl.toml +0 -0
  25. {rumdl-0.0.74 → rumdl-0.0.76}/MANIFEST.in +0 -0
  26. {rumdl-0.0.74 → rumdl-0.0.76}/Makefile +0 -0
  27. {rumdl-0.0.74 → rumdl-0.0.76}/README.md +0 -0
  28. {rumdl-0.0.74 → rumdl-0.0.76}/assets/logo.png +0 -0
  29. {rumdl-0.0.74 → rumdl-0.0.76}/benches/fix_performance.rs +0 -0
  30. {rumdl-0.0.74 → rumdl-0.0.76}/benches/range_performance.rs +0 -0
  31. {rumdl-0.0.74 → rumdl-0.0.76}/benches/range_utils_benchmark.rs +0 -0
  32. {rumdl-0.0.74 → rumdl-0.0.76}/benches/rule_performance.rs +0 -0
  33. {rumdl-0.0.74 → rumdl-0.0.76}/benches/simple_fix_bench.rs +0 -0
  34. {rumdl-0.0.74 → rumdl-0.0.76}/docs/RULES.md +0 -0
  35. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md001.md +0 -0
  36. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md002.md +0 -0
  37. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md003.md +0 -0
  38. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md004.md +0 -0
  39. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md005.md +0 -0
  40. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md006.md +0 -0
  41. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md007.md +0 -0
  42. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md009.md +0 -0
  43. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md010.md +0 -0
  44. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md011.md +0 -0
  45. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md012.md +0 -0
  46. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md013.md +0 -0
  47. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md014.md +0 -0
  48. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md018.md +0 -0
  49. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md019.md +0 -0
  50. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md020.md +0 -0
  51. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md021.md +0 -0
  52. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md022.md +0 -0
  53. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md023.md +0 -0
  54. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md024.md +0 -0
  55. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md025.md +0 -0
  56. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md026.md +0 -0
  57. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md027.md +0 -0
  58. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md028.md +0 -0
  59. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md029.md +0 -0
  60. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md030.md +0 -0
  61. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md031.md +0 -0
  62. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md032.md +0 -0
  63. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md033.md +0 -0
  64. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md034.md +0 -0
  65. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md035.md +0 -0
  66. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md036.md +0 -0
  67. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md037.md +0 -0
  68. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md038.md +0 -0
  69. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md039.md +0 -0
  70. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md040.md +0 -0
  71. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md041.md +0 -0
  72. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md042.md +0 -0
  73. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md043.md +0 -0
  74. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md044.md +0 -0
  75. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md045.md +0 -0
  76. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md046.md +0 -0
  77. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md047.md +0 -0
  78. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md048.md +0 -0
  79. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md049.md +0 -0
  80. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md050.md +0 -0
  81. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md051.md +0 -0
  82. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md052.md +0 -0
  83. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md053.md +0 -0
  84. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md054.md +0 -0
  85. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md055.md +0 -0
  86. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md056.md +0 -0
  87. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md057.md +0 -0
  88. {rumdl-0.0.74 → rumdl-0.0.76}/docs/md058.md +0 -0
  89. {rumdl-0.0.74 → rumdl-0.0.76}/issues/plan-rule-parity-with-markdownlint.md +0 -0
  90. {rumdl-0.0.74 → rumdl-0.0.76}/parity_check.py +0 -0
  91. {rumdl-0.0.74 → rumdl-0.0.76}/pyproject.toml +0 -0
  92. {rumdl-0.0.74 → rumdl-0.0.76}/python/MANIFEST.in +0 -0
  93. {rumdl-0.0.74 → rumdl-0.0.76}/python/PYTHON-README.md +0 -0
  94. {rumdl-0.0.74 → rumdl-0.0.76}/python/rumdl/__init__.py +0 -0
  95. {rumdl-0.0.74 → rumdl-0.0.76}/python/rumdl/__main__.py +0 -0
  96. {rumdl-0.0.74 → rumdl-0.0.76}/python/rumdl/py.typed +0 -0
  97. {rumdl-0.0.74 → rumdl-0.0.76}/rumdl.toml.example +0 -0
  98. {rumdl-0.0.74 → rumdl-0.0.76}/src/config.rs +0 -0
  99. {rumdl-0.0.74 → rumdl-0.0.76}/src/init.rs +0 -0
  100. {rumdl-0.0.74 → rumdl-0.0.76}/src/lib.rs +0 -0
  101. {rumdl-0.0.74 → rumdl-0.0.76}/src/lint_context.rs +0 -0
  102. {rumdl-0.0.74 → rumdl-0.0.76}/src/lsp/mod.rs +0 -0
  103. {rumdl-0.0.74 → rumdl-0.0.76}/src/lsp/server.rs +0 -0
  104. {rumdl-0.0.74 → rumdl-0.0.76}/src/lsp/types.rs +0 -0
  105. {rumdl-0.0.74 → rumdl-0.0.76}/src/main.rs +0 -0
  106. {rumdl-0.0.74 → rumdl-0.0.76}/src/markdownlint_config.rs +0 -0
  107. {rumdl-0.0.74 → rumdl-0.0.76}/src/parallel.rs +0 -0
  108. {rumdl-0.0.74 → rumdl-0.0.76}/src/performance.rs +0 -0
  109. {rumdl-0.0.74 → rumdl-0.0.76}/src/profiling.rs +0 -0
  110. {rumdl-0.0.74 → rumdl-0.0.76}/src/python.rs +0 -0
  111. {rumdl-0.0.74 → rumdl-0.0.76}/src/rule.rs +0 -0
  112. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/blockquote_utils.rs +0 -0
  113. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/code_block_utils.rs +0 -0
  114. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/code_fence_utils.rs +0 -0
  115. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/emphasis_style.rs +0 -0
  116. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/front_matter_utils.rs +0 -0
  117. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/heading_utils.rs +0 -0
  118. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/list_utils.rs +0 -0
  119. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md001_heading_increment.rs +0 -0
  120. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md002_first_heading_h1.rs +0 -0
  121. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md003_heading_style.rs +0 -0
  122. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md004_unordered_list_style.rs +0 -0
  123. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md007_ul_indent.rs +0 -0
  124. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md009_trailing_spaces.rs +0 -0
  125. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md010_no_hard_tabs.rs +0 -0
  126. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md012_no_multiple_blanks.rs +0 -0
  127. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md013_line_length.rs +0 -0
  128. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md014_commands_show_output.rs +0 -0
  129. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md020_no_missing_space_closed_atx.rs +0 -0
  130. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md022_blanks_around_headings.rs +0 -0
  131. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md024_no_duplicate_heading.rs +0 -0
  132. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md025_single_title.rs +0 -0
  133. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md026_no_trailing_punctuation.rs +0 -0
  134. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md029_ordered_list_prefix.rs +0 -0
  135. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md033_no_inline_html.rs +0 -0
  136. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md034_no_bare_urls.rs +0 -0
  137. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md035_hr_style.rs +0 -0
  138. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md036_no_emphasis_only_first.rs +0 -0
  139. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md038_no_space_in_code.rs +0 -0
  140. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md039_no_space_in_links.rs +0 -0
  141. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md040_fenced_code_language.rs +0 -0
  142. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md041_first_line_heading.rs +0 -0
  143. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md042_no_empty_links.rs +0 -0
  144. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md043_required_headings.rs +0 -0
  145. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md044_proper_names.rs +0 -0
  146. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md045_no_alt_text.rs +0 -0
  147. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md046_code_block_style.rs +0 -0
  148. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md047_single_trailing_newline.rs +0 -0
  149. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md048_code_fence_style.rs +0 -0
  150. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md049_emphasis_style.rs +0 -0
  151. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md050_strong_style.rs +0 -0
  152. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md051_link_fragments.rs +0 -0
  153. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md052_reference_links_images.rs +0 -0
  154. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md053_link_image_reference_definitions.rs +0 -0
  155. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md054_link_image_style.rs +0 -0
  156. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md055_table_pipe_style.rs +0 -0
  157. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md056_table_column_count.rs +0 -0
  158. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md057_existing_relative_links.rs +0 -0
  159. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/md058_blanks_around_tables.rs +0 -0
  160. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/mod.rs +0 -0
  161. {rumdl-0.0.74 → rumdl-0.0.76}/src/rules/strong_style.rs +0 -0
  162. {rumdl-0.0.74 → rumdl-0.0.76}/src/utils/ast_utils.rs +0 -0
  163. {rumdl-0.0.74 → rumdl-0.0.76}/src/utils/code_block_utils.rs +0 -0
  164. {rumdl-0.0.74 → rumdl-0.0.76}/src/utils/document_structure.rs +0 -0
  165. {rumdl-0.0.74 → rumdl-0.0.76}/src/utils/early_returns.rs +0 -0
  166. {rumdl-0.0.74 → rumdl-0.0.76}/src/utils/element_cache.rs +0 -0
  167. {rumdl-0.0.74 → rumdl-0.0.76}/src/utils/markdown_elements.rs +0 -0
  168. {rumdl-0.0.74 → rumdl-0.0.76}/src/utils/mod.rs +0 -0
  169. {rumdl-0.0.74 → rumdl-0.0.76}/src/utils/range_utils.rs +0 -0
  170. {rumdl-0.0.74 → rumdl-0.0.76}/src/utils/regex_cache.rs +0 -0
  171. {rumdl-0.0.74 → rumdl-0.0.76}/src/utils/string_interner.rs +0 -0
  172. {rumdl-0.0.74 → rumdl-0.0.76}/src/utils/table_utils.rs +0 -0
  173. {rumdl-0.0.74 → rumdl-0.0.76}/tests/advanced_integration_tests.rs +0 -0
  174. {rumdl-0.0.74 → rumdl-0.0.76}/tests/character_ranges/additional_tests.rs +0 -0
  175. {rumdl-0.0.74 → rumdl-0.0.76}/tests/character_ranges/comprehensive_tests.rs +0 -0
  176. {rumdl-0.0.74 → rumdl-0.0.76}/tests/character_ranges/mod.rs +0 -0
  177. {rumdl-0.0.74 → rumdl-0.0.76}/tests/character_ranges/unicode_utils.rs +0 -0
  178. {rumdl-0.0.74 → rumdl-0.0.76}/tests/cli_duplication_test.rs +0 -0
  179. {rumdl-0.0.74 → rumdl-0.0.76}/tests/cli_integration_tests.rs +0 -0
  180. {rumdl-0.0.74 → rumdl-0.0.76}/tests/commonmark_compliance_tests.rs +0 -0
  181. {rumdl-0.0.74 → rumdl-0.0.76}/tests/comprehensive_integration_tests.rs +0 -0
  182. {rumdl-0.0.74 → rumdl-0.0.76}/tests/config_application_tests.rs +0 -0
  183. {rumdl-0.0.74 → rumdl-0.0.76}/tests/config_tests.rs +0 -0
  184. {rumdl-0.0.74 → rumdl-0.0.76}/tests/init_command_test.rs +0 -0
  185. {rumdl-0.0.74 → rumdl-0.0.76}/tests/init_tests.rs +0 -0
  186. {rumdl-0.0.74 → rumdl-0.0.76}/tests/integration_tests.rs +0 -0
  187. {rumdl-0.0.74 → rumdl-0.0.76}/tests/json_output_test.rs +0 -0
  188. {rumdl-0.0.74 → rumdl-0.0.76}/tests/lib.rs +0 -0
  189. {rumdl-0.0.74 → rumdl-0.0.76}/tests/lsp_integration_tests.rs +0 -0
  190. {rumdl-0.0.74 → rumdl-0.0.76}/tests/lsp_tests.rs +0 -0
  191. {rumdl-0.0.74 → rumdl-0.0.76}/tests/markdownlint_cli_integration.rs +0 -0
  192. {rumdl-0.0.74 → rumdl-0.0.76}/tests/markdownlint_config_test.rs +0 -0
  193. {rumdl-0.0.74 → rumdl-0.0.76}/tests/md030_edge_cases.md +0 -0
  194. {rumdl-0.0.74 → rumdl-0.0.76}/tests/output_format_tests.rs +0 -0
  195. {rumdl-0.0.74 → rumdl-0.0.76}/tests/perf_check.rs +0 -0
  196. {rumdl-0.0.74 → rumdl-0.0.76}/tests/pyproject_config_tests.rs +0 -0
  197. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md001_test.rs +0 -0
  198. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md001_unicode_test.rs +0 -0
  199. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md002_test.rs +0 -0
  200. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md003_test.rs +0 -0
  201. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md004_test.rs +0 -0
  202. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md005_test.rs +0 -0
  203. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md006_test.rs +0 -0
  204. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md006_unicode_test.rs +0 -0
  205. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md007_test.rs +0 -0
  206. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md009_test.rs +0 -0
  207. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md010_test.rs +0 -0
  208. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md011_test.rs +0 -0
  209. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md012_test.rs +0 -0
  210. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md013_test.rs +0 -0
  211. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md014_test.rs +0 -0
  212. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md018_test.rs +0 -0
  213. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md020_test.rs +0 -0
  214. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md021_test.rs +0 -0
  215. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md022_test.rs +0 -0
  216. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md023_extended_test.rs +0 -0
  217. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md023_test.rs +0 -0
  218. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md024_test.rs +0 -0
  219. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md025_test.rs +0 -0
  220. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md026_test.rs +0 -0
  221. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md027_test.rs +0 -0
  222. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md028_test.rs +0 -0
  223. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md029_test.rs +0 -0
  224. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md030_test.rs +0 -0
  225. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md031_test.rs +0 -0
  226. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md032_test.rs +0 -0
  227. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md033_extended_test.rs +0 -0
  228. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md033_test.rs +0 -0
  229. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md034_test.rs +0 -0
  230. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md035_test.rs +0 -0
  231. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md036_test.rs +0 -0
  232. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md037_test.rs +0 -0
  233. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md038_test.rs +0 -0
  234. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md039_test.rs +0 -0
  235. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md040_test.rs +0 -0
  236. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md041_test.rs +0 -0
  237. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md042_test.rs +0 -0
  238. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md043_test.rs +0 -0
  239. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md044_test.rs +0 -0
  240. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md045_test.rs +0 -0
  241. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md046_test.rs +0 -0
  242. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md047_test.rs +0 -0
  243. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md048_test.rs +0 -0
  244. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md049_test.rs +0 -0
  245. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md050_test.rs +0 -0
  246. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md051_test.rs +0 -0
  247. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md052_test.rs +0 -0
  248. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md053_additional_test.rs +0 -0
  249. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md053_proptest.rs +0 -0
  250. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md053_test.rs +0 -0
  251. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md054_test.rs +0 -0
  252. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md054_unicode_test.rs +0 -0
  253. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md055_test.rs +0 -0
  254. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md056_test.rs +0 -0
  255. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md057_test.rs +0 -0
  256. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/md058_test.rs +0 -0
  257. {rumdl-0.0.74 → rumdl-0.0.76}/tests/rules/mod.rs +0 -0
  258. {rumdl-0.0.74 → rumdl-0.0.76}/tests/utils/blockquote_utils_test.rs +0 -0
  259. {rumdl-0.0.74 → rumdl-0.0.76}/tests/utils/code_block_utils_extended_test.rs +0 -0
  260. {rumdl-0.0.74 → rumdl-0.0.76}/tests/utils/code_block_utils_test.rs +0 -0
  261. {rumdl-0.0.74 → rumdl-0.0.76}/tests/utils/core_utils_test.rs +0 -0
  262. {rumdl-0.0.74 → rumdl-0.0.76}/tests/utils/front_matter_utils_test.rs +0 -0
  263. {rumdl-0.0.74 → rumdl-0.0.76}/tests/utils/line_index_test.rs +0 -0
  264. {rumdl-0.0.74 → rumdl-0.0.76}/tests/utils/mod.rs +0 -0
  265. {rumdl-0.0.74 → rumdl-0.0.76}/tests/utils_markdown_edge_cases.rs +0 -0
  266. {rumdl-0.0.74 → rumdl-0.0.76}/tests/utils_tests.rs +0 -0
@@ -1789,7 +1789,7 @@ dependencies = [
1789
1789
 
1790
1790
  [[package]]
1791
1791
  name = "rumdl"
1792
- version = "0.0.74"
1792
+ version = "0.0.76"
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.74"
3
+ version = "0.0.76"
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.74
3
+ Version: 0.0.76
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -96,6 +96,12 @@ impl MD005ListIndent {
96
96
  }
97
97
  }
98
98
 
99
+ impl Default for MD005ListIndent {
100
+ fn default() -> Self {
101
+ Self
102
+ }
103
+ }
104
+
99
105
  impl Rule for MD005ListIndent {
100
106
  fn name(&self) -> &'static str {
101
107
  "MD005"
@@ -237,20 +237,21 @@ impl Rule for MD006StartBullets {
237
237
  valid_bullet_lines[line_idx] = is_valid;
238
238
 
239
239
  if !is_valid {
240
- let fixed_line = line.trim_start();
241
- let needs_blank_line = line_idx > 0
242
- && !Self::is_blank_line(lines[line_idx - 1])
243
- && Self::is_bullet_list_item(lines[line_idx - 1]).is_none();
244
- let replacement = if needs_blank_line {
245
- format!("\n{}", fixed_line)
240
+ // Calculate the precise range for the indentation that needs to be removed
241
+ // For " * Indented bullet", we want to highlight just the indentation and marker " *" (columns 1-3)
242
+ let start_col = 1; // Start from beginning of line
243
+ let end_col = indent + 2; // Include the marker (indent + 1 for marker position + 1 for inclusive range)
244
+
245
+ // For the fix, we need to replace the highlighted part (" *") with just the bullet marker ("* ")
246
+ let line = lines[line_idx];
247
+ let trimmed = line.trim_start();
248
+ // Extract just the bullet marker and normalize to single space
249
+ let bullet_part = if let Some(captures) = BULLET_PATTERN.captures(trimmed) {
250
+ format!("{} ", captures.get(2).unwrap().as_str()) // Always use single space
246
251
  } else {
247
- fixed_line.to_string()
252
+ "* ".to_string() // fallback
248
253
  };
249
-
250
- // Calculate the range to highlight: from first indentation character to end of list marker
251
- let start_col = if indent > 0 { 2 } else { 1 }; // Start from first indentation space if indented
252
- let marker_pos = line.find(|c: char| c == '*' || c == '-' || c == '+').unwrap_or(0);
253
- let end_col = marker_pos + 3; // +1 for the marker itself, +1 for 1-based indexing, +1 for space after marker
254
+ let replacement = bullet_part;
254
255
 
255
256
  result.push(LintWarning {
256
257
  rule_name: Some(self.name()),
@@ -261,7 +262,7 @@ impl Rule for MD006StartBullets {
261
262
  end_column: end_col,
262
263
  message: "List item indentation".to_string(),
263
264
  fix: Some(Fix {
264
- range: line_index.line_col_to_byte_range(line_num, 1),
265
+ range: line_index.line_col_to_byte_range(line_num, start_col),
265
266
  replacement,
266
267
  }),
267
268
  });
@@ -0,0 +1,453 @@
1
+ /// Rule MD011: No reversed link syntax
2
+ ///
3
+ /// See [docs/md011.md](../../docs/md011.md) for full documentation, configuration, and examples.
4
+ use crate::rule::{Fix, LintError, LintResult, LintWarning, Rule, Severity};
5
+ use crate::utils::range_utils::calculate_match_range;
6
+ use lazy_static::lazy_static;
7
+ use regex::Regex;
8
+
9
+ lazy_static! {
10
+ static ref REVERSED_LINK_REGEX: Regex =
11
+ Regex::new(r"\[([^\]]+)\]\(([^)]+)\)|(\([^)]+\))\[([^\]]+)\]").unwrap();
12
+ static ref REVERSED_LINK_CHECK_REGEX: Regex = Regex::new(r"\(([^)]+)\)\[([^\]]+)\]").unwrap();
13
+ static ref CODE_FENCE_REGEX: Regex = Regex::new(r"^(\s*)(```|~~~)").unwrap();
14
+
15
+ // New patterns for detecting malformed link attempts where user intent is clear
16
+ static ref MALFORMED_LINK_PATTERNS: Vec<(Regex, &'static str)> = vec![
17
+ // Missing closing bracket: (URL)[text or [text](URL
18
+ (Regex::new(r"\(([^)]+)\)\[([^\]]*$)").unwrap(), "missing closing bracket"),
19
+ (Regex::new(r"\[([^\]]+)\]\(([^)]*$)").unwrap(), "missing closing parenthesis"),
20
+
21
+ // Wrong bracket types: {URL}[text] or [text]{URL}
22
+ (Regex::new(r"\{([^}]+)\}\[([^\]]+)\]").unwrap(), "wrong bracket type (curly instead of parentheses)"),
23
+ (Regex::new(r"\[([^\]]+)\]\{([^}]+)\}").unwrap(), "wrong bracket type (curly instead of parentheses)"),
24
+
25
+ // URL and text swapped in correct syntax: [URL](text) where URL is clearly a URL
26
+ (Regex::new(r"\[(https?://[^\]]+)\]\(([^)]+)\)").unwrap(), "URL and text appear to be swapped"),
27
+ (Regex::new(r"\[(www\.[^\]]+)\]\(([^)]+)\)").unwrap(), "URL and text appear to be swapped"),
28
+ (Regex::new(r"\[([^\]]*\.[a-z]{2,4}[^\]]*)\]\(([^)]+)\)").unwrap(), "URL and text appear to be swapped"),
29
+ ];
30
+ }
31
+
32
+ #[derive(Clone)]
33
+ pub struct MD011NoReversedLinks;
34
+
35
+ impl MD011NoReversedLinks {
36
+ fn find_reversed_links(content: &str) -> Vec<(usize, usize, String, String)> {
37
+ let mut results = Vec::new();
38
+ let mut line_start = 0;
39
+ let mut current_line = 1;
40
+
41
+ for line in content.lines() {
42
+ for cap in REVERSED_LINK_REGEX.captures_iter(line) {
43
+ if cap.get(3).is_some() {
44
+ // Found reversed link syntax (text)[url]
45
+ let text = cap[3].trim_matches('(').trim_matches(')');
46
+ let url = &cap[4];
47
+ let start = line_start + cap.get(0).unwrap().start();
48
+ results.push((
49
+ current_line,
50
+ start - line_start + 1,
51
+ text.to_string(),
52
+ url.to_string(),
53
+ ));
54
+ }
55
+ }
56
+ line_start += line.len() + 1; // +1 for newline
57
+ current_line += 1;
58
+ }
59
+
60
+ results
61
+ }
62
+
63
+ /// Detect malformed link attempts where user intent is clear
64
+ fn detect_malformed_link_attempts(&self, line: &str) -> Vec<(usize, usize, String, String)> {
65
+ let mut results = Vec::new();
66
+ let mut processed_ranges = Vec::new(); // Track processed character ranges to avoid duplicates
67
+
68
+ for (pattern, issue_type) in MALFORMED_LINK_PATTERNS.iter() {
69
+ for cap in pattern.captures_iter(line) {
70
+ let match_obj = cap.get(0).unwrap();
71
+ let start = match_obj.start();
72
+ let len = match_obj.len();
73
+ let end = start + len;
74
+
75
+ // Skip if this range overlaps with already processed ranges
76
+ if processed_ranges.iter().any(|(proc_start, proc_end)|
77
+ (start < *proc_end && end > *proc_start)
78
+ ) {
79
+ continue;
80
+ }
81
+
82
+ // Extract potential URL and text based on the pattern
83
+ if let Some((url, text)) = self.extract_url_and_text_from_match(&cap, issue_type) {
84
+ // Only proceed if this looks like a genuine link attempt
85
+ if self.looks_like_link_attempt(&url, &text) {
86
+ results.push((start, len, url, text));
87
+ processed_ranges.push((start, end));
88
+ }
89
+ }
90
+ }
91
+ }
92
+
93
+ results
94
+ }
95
+
96
+ /// Extract URL and text from regex match based on the issue type
97
+ fn extract_url_and_text_from_match(&self, cap: &regex::Captures, issue_type: &str) -> Option<(String, String)> {
98
+ match issue_type {
99
+ "missing closing bracket" => {
100
+ // (URL)[text -> cap[1] = URL, cap[2] = incomplete text
101
+ Some((cap[1].to_string(), format!("{}]", &cap[2])))
102
+ },
103
+ "missing closing parenthesis" => {
104
+ // [text](URL -> cap[1] = text, cap[2] = incomplete URL
105
+ Some((format!("{})", &cap[2]), cap[1].to_string()))
106
+ },
107
+ "wrong bracket type (curly instead of parentheses)" => {
108
+ // {URL}[text] or [text]{URL} -> cap[1] and cap[2]
109
+ if cap.get(0).unwrap().as_str().starts_with('{') {
110
+ // {URL}[text] -> swap and fix brackets
111
+ Some((cap[1].to_string(), cap[2].to_string()))
112
+ } else {
113
+ // [text]{URL} -> already in correct order, fix brackets
114
+ Some((cap[2].to_string(), cap[1].to_string()))
115
+ }
116
+ },
117
+ "URL and text appear to be swapped" => {
118
+ // [URL](text) -> cap[1] = URL, cap[2] = text, need to swap
119
+ Some((cap[1].to_string(), cap[2].to_string()))
120
+ },
121
+ _ => None,
122
+ }
123
+ }
124
+
125
+ /// Check if the extracted URL and text look like a genuine link attempt
126
+ fn looks_like_link_attempt(&self, url: &str, text: &str) -> bool {
127
+ // URL should look like a URL
128
+ let url_indicators = [
129
+ "http://", "https://", "www.", "ftp://",
130
+ ".com", ".org", ".net", ".edu", ".gov", ".io", ".co"
131
+ ];
132
+
133
+ let has_url_indicator = url_indicators.iter().any(|indicator|
134
+ url.to_lowercase().contains(indicator)
135
+ );
136
+
137
+ // Text should be reasonable length and not look like a URL
138
+ let text_looks_reasonable = text.len() >= 3 && text.len() <= 50
139
+ && !url_indicators.iter().any(|indicator|
140
+ text.to_lowercase().contains(indicator)
141
+ )
142
+ && !text.to_lowercase().starts_with("http")
143
+ && text.chars().any(|c| c.is_alphabetic()); // Must contain at least one letter
144
+
145
+ // URL should not be too short or contain only non-URL characters
146
+ let url_looks_reasonable = url.len() >= 4
147
+ && (has_url_indicator || url.contains('.'))
148
+ && !url.chars().all(|c| c.is_alphabetic()); // Shouldn't be just letters
149
+
150
+ // Both URL and text should look reasonable for this to be a link attempt
151
+ has_url_indicator && text_looks_reasonable && url_looks_reasonable
152
+ }
153
+
154
+ fn is_in_code_block(&self, content: &str, position: usize) -> bool {
155
+ let mut in_code_block = false;
156
+ let mut current_pos = 0;
157
+
158
+ for line in content.lines() {
159
+ if CODE_FENCE_REGEX.is_match(line) {
160
+ in_code_block = !in_code_block;
161
+ }
162
+ current_pos += line.len() + 1;
163
+ if current_pos > position {
164
+ break;
165
+ }
166
+ }
167
+
168
+ in_code_block
169
+ }
170
+ }
171
+
172
+ impl Default for MD011NoReversedLinks {
173
+ fn default() -> Self {
174
+ Self
175
+ }
176
+ }
177
+
178
+ impl Rule for MD011NoReversedLinks {
179
+ fn name(&self) -> &'static str {
180
+ "MD011"
181
+ }
182
+
183
+ fn description(&self) -> &'static str {
184
+ "Link syntax should not be reversed"
185
+ }
186
+
187
+ fn check(&self, ctx: &crate::lint_context::LintContext) -> LintResult {
188
+ let content = ctx.content;
189
+ let mut warnings = Vec::new();
190
+
191
+ for (line_num, line) in content.lines().enumerate() {
192
+ // Part 1: Check for existing perfectly formed reversed links
193
+ for cap in REVERSED_LINK_CHECK_REGEX.captures_iter(line) {
194
+ let match_obj = cap.get(0).unwrap();
195
+
196
+ // Calculate precise character range for the reversed syntax
197
+ let (start_line, start_col, end_line, end_col) =
198
+ calculate_match_range(line_num + 1, line, match_obj.start(), match_obj.len());
199
+
200
+ warnings.push(LintWarning {
201
+ rule_name: Some(self.name()),
202
+ message: "Reversed link syntax".to_string(),
203
+ line: start_line,
204
+ column: start_col,
205
+ end_line,
206
+ end_column: end_col,
207
+ severity: Severity::Warning,
208
+ fix: Some(Fix {
209
+ range: (0..0), // TODO: Replace with correct byte range if available
210
+ replacement: format!("[{}]({})", &cap[2], &cap[1]),
211
+ }),
212
+ });
213
+ }
214
+
215
+ // Part 2: Check for malformed link attempts where user intent is clear
216
+ let malformed_attempts = self.detect_malformed_link_attempts(line);
217
+ for (start, len, url, text) in malformed_attempts {
218
+ // Calculate precise character range for the malformed syntax
219
+ let (start_line, start_col, end_line, end_col) =
220
+ calculate_match_range(line_num + 1, line, start, len);
221
+
222
+ warnings.push(LintWarning {
223
+ rule_name: Some(self.name()),
224
+ message: "Malformed link syntax".to_string(),
225
+ line: start_line,
226
+ column: start_col,
227
+ end_line,
228
+ end_column: end_col,
229
+ severity: Severity::Warning,
230
+ fix: Some(Fix {
231
+ range: (0..0), // TODO: Replace with correct byte range if available
232
+ replacement: format!("[{}]({})", text, url),
233
+ }),
234
+ });
235
+ }
236
+ }
237
+
238
+ Ok(warnings)
239
+ }
240
+
241
+ fn fix(&self, ctx: &crate::lint_context::LintContext) -> Result<String, LintError> {
242
+ let content = ctx.content;
243
+ let mut result = content.to_string();
244
+ let mut offset: usize = 0;
245
+
246
+ for (line_num, column, text, url) in Self::find_reversed_links(content) {
247
+ // Calculate absolute position in original content
248
+ let mut pos = 0;
249
+ for (i, line) in content.lines().enumerate() {
250
+ if i + 1 == line_num {
251
+ pos += column - 1;
252
+ break;
253
+ }
254
+ pos += line.len() + 1;
255
+ }
256
+
257
+ if !self.is_in_code_block(content, pos) {
258
+ let adjusted_pos = pos + offset;
259
+ let original_len = format!("({})[{}]", url, text).len();
260
+ let replacement = format!("[{}]({})", text, url);
261
+ result.replace_range(adjusted_pos..adjusted_pos + original_len, &replacement);
262
+ // Update offset based on the difference in lengths
263
+ if replacement.len() > original_len {
264
+ offset += replacement.len() - original_len;
265
+ } else {
266
+ offset = offset.saturating_sub(original_len - replacement.len());
267
+ }
268
+ }
269
+ }
270
+
271
+ Ok(result)
272
+ }
273
+
274
+ fn as_any(&self) -> &dyn std::any::Any {
275
+ self
276
+ }
277
+
278
+ fn from_config(_config: &crate::config::Config) -> Box<dyn Rule>
279
+ where
280
+ Self: Sized,
281
+ {
282
+ Box::new(MD011NoReversedLinks)
283
+ }
284
+ }
285
+
286
+ #[cfg(test)]
287
+ mod tests {
288
+ use super::*;
289
+ use crate::lint_context::LintContext;
290
+
291
+ #[test]
292
+ fn test_capture_group_order_fix() {
293
+ // This test confirms that the capture group order bug is fixed
294
+ // The regex pattern \(([^)]+)\)\[([^\]]+)\] captures:
295
+ // cap[1] = URL (inside parentheses)
296
+ // cap[2] = text (inside brackets)
297
+ // So (URL)[text] should become [text](URL)
298
+
299
+ let rule = MD011NoReversedLinks;
300
+
301
+ // Test with reversed link syntax
302
+ let content = "Check out (https://example.com)[this link] for more info.";
303
+ let ctx = LintContext::new(content);
304
+
305
+ // This should detect the reversed syntax
306
+ let result = rule.check(&ctx).unwrap();
307
+ assert_eq!(result.len(), 1);
308
+ assert!(result[0].message.contains("Reversed link syntax"));
309
+
310
+ // Verify the fix produces correct output
311
+ let fix = result[0].fix.as_ref().unwrap();
312
+ assert_eq!(fix.replacement, "[this link](https://example.com)");
313
+ }
314
+
315
+ #[test]
316
+ fn test_multiple_reversed_links() {
317
+ // Test multiple reversed links in the same content
318
+ let rule = MD011NoReversedLinks;
319
+
320
+ let content = "Visit (https://example.com)[Example] and (https://test.com)[Test Site].";
321
+ let ctx = LintContext::new(content);
322
+
323
+ let result = rule.check(&ctx).unwrap();
324
+ assert_eq!(result.len(), 2);
325
+
326
+ // Verify both fixes are correct
327
+ assert_eq!(result[0].fix.as_ref().unwrap().replacement, "[Example](https://example.com)");
328
+ assert_eq!(result[1].fix.as_ref().unwrap().replacement, "[Test Site](https://test.com)");
329
+ }
330
+
331
+ #[test]
332
+ fn test_normal_links_not_flagged() {
333
+ // Test that normal link syntax is not flagged
334
+ let rule = MD011NoReversedLinks;
335
+
336
+ let content = "This is a normal [link](https://example.com) and another [link](https://test.com).";
337
+ let ctx = LintContext::new(content);
338
+
339
+ let result = rule.check(&ctx).unwrap();
340
+ assert_eq!(result.len(), 0);
341
+ }
342
+
343
+ #[test]
344
+ fn debug_capture_groups() {
345
+ // Debug test to understand capture group behavior
346
+ let pattern = r"\(([^)]+)\)\[([^\]]+)\]";
347
+ let regex = Regex::new(pattern).unwrap();
348
+
349
+ let test_text = "(https://example.com)[Click here]";
350
+
351
+ if let Some(cap) = regex.captures(test_text) {
352
+ println!("Full match: {}", &cap[0]);
353
+ println!("cap[1] (first group): {}", &cap[1]);
354
+ println!("cap[2] (second group): {}", &cap[2]);
355
+
356
+ // Current fix format
357
+ let current_fix = format!("[{}]({})", &cap[2], &cap[1]);
358
+ println!("Current fix produces: {}", current_fix);
359
+
360
+ // Test what the actual rule produces
361
+ let rule = MD011NoReversedLinks;
362
+ let ctx = LintContext::new(test_text);
363
+ let result = rule.check(&ctx).unwrap();
364
+ if !result.is_empty() {
365
+ println!("Rule fix produces: {}", result[0].fix.as_ref().unwrap().replacement);
366
+ }
367
+ }
368
+ }
369
+
370
+ #[test]
371
+ fn test_malformed_link_detection() {
372
+ let rule = MD011NoReversedLinks;
373
+
374
+ // Test wrong bracket types
375
+ let content = "Check out {https://example.com}[this website].";
376
+ let ctx = LintContext::new(content);
377
+ let result = rule.check(&ctx).unwrap();
378
+ assert_eq!(result.len(), 1);
379
+ assert!(result[0].message.contains("Malformed link syntax"));
380
+
381
+ // Test URL and text swapped
382
+ let content = "Visit [https://example.com](Click Here).";
383
+ let ctx = LintContext::new(content);
384
+ let result = rule.check(&ctx).unwrap();
385
+ assert_eq!(result.len(), 1);
386
+ assert!(result[0].message.contains("Malformed link syntax"));
387
+
388
+ // Test that valid links are not flagged
389
+ let content = "This is a [normal link](https://example.com).";
390
+ let ctx = LintContext::new(content);
391
+ let result = rule.check(&ctx).unwrap();
392
+ assert_eq!(result.len(), 0);
393
+
394
+ // Test that non-links are not flagged
395
+ let content = "Regular text with [brackets] and (parentheses).";
396
+ let ctx = LintContext::new(content);
397
+ let result = rule.check(&ctx).unwrap();
398
+ assert_eq!(result.len(), 0);
399
+
400
+ // Test that risky patterns are NOT flagged (conservative approach)
401
+ let content = "(example.com)is a test domain.";
402
+ let ctx = LintContext::new(content);
403
+ let result = rule.check(&ctx).unwrap();
404
+ assert_eq!(result.len(), 0);
405
+
406
+ let content = "(optional)parameter should not be flagged.";
407
+ let ctx = LintContext::new(content);
408
+ let result = rule.check(&ctx).unwrap();
409
+ assert_eq!(result.len(), 0);
410
+ }
411
+
412
+ #[test]
413
+ fn test_malformed_link_fixes() {
414
+ let rule = MD011NoReversedLinks;
415
+
416
+ // Test wrong bracket types fix
417
+ let content = "Check out {https://example.com}[this website].";
418
+ let ctx = LintContext::new(content);
419
+ let result = rule.check(&ctx).unwrap();
420
+ assert_eq!(result.len(), 1);
421
+ let fix = result[0].fix.as_ref().unwrap();
422
+ assert_eq!(fix.replacement, "[this website](https://example.com)");
423
+
424
+ // Test URL and text swapped fix
425
+ let content = "Visit [https://example.com](Click Here).";
426
+ let ctx = LintContext::new(content);
427
+ let result = rule.check(&ctx).unwrap();
428
+ assert_eq!(result.len(), 1);
429
+ let fix = result[0].fix.as_ref().unwrap();
430
+ assert_eq!(fix.replacement, "[Click Here](https://example.com)");
431
+ }
432
+
433
+ #[test]
434
+ fn test_conservative_detection() {
435
+ let rule = MD011NoReversedLinks;
436
+
437
+ // Test that edge cases are not flagged
438
+ let content = "This (not-a-url)text should be ignored.";
439
+ let ctx = LintContext::new(content);
440
+ let result = rule.check(&ctx).unwrap();
441
+ assert_eq!(result.len(), 0);
442
+
443
+ let content = "Also [regular text](not a url) should be ignored.";
444
+ let ctx = LintContext::new(content);
445
+ let result = rule.check(&ctx).unwrap();
446
+ assert_eq!(result.len(), 0);
447
+
448
+ let content = "And {not-url}[not-text] should be ignored.";
449
+ let ctx = LintContext::new(content);
450
+ let result = rule.check(&ctx).unwrap();
451
+ assert_eq!(result.len(), 0);
452
+ }
453
+ }