rumdl 0.0.66__tar.gz → 0.0.68__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.
- {rumdl-0.0.66 → rumdl-0.0.68}/Cargo.lock +17 -1
- {rumdl-0.0.66 → rumdl-0.0.68}/Cargo.toml +5 -1
- {rumdl-0.0.66 → rumdl-0.0.68}/PKG-INFO +1 -1
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md033.md +9 -1
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md036.md +11 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/lsp/types.rs +13 -4
- {rumdl-0.0.66 → rumdl-0.0.68}/src/main.rs +91 -1
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rule.rs +4 -2
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md001_heading_increment.rs +16 -3
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md002_first_heading_h1.rs +18 -2
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md003_heading_style.rs +97 -139
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md004_unordered_list_style.rs +10 -6
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md005_list_indent.rs +94 -55
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md006_start_bullets.rs +28 -20
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md007_ul_indent.rs +55 -75
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md009_trailing_spaces.rs +40 -22
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md010_no_hard_tabs.rs +67 -24
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md011_no_reversed_links.rs +16 -11
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md012_no_multiple_blanks.rs +29 -13
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md013_line_length.rs +215 -44
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md014_commands_show_output.rs +37 -18
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md018_no_missing_space_atx.rs +18 -3
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md019_no_multiple_space_atx.rs +19 -8
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md020_no_missing_space_closed_atx.rs +50 -22
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md021_no_multiple_space_closed_atx.rs +40 -10
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md022_blanks_around_headings.rs +36 -18
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md023_heading_start_left.rs +104 -46
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md024_no_duplicate_heading.rs +34 -9
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md025_single_title.rs +44 -6
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md026_no_trailing_punctuation.rs +85 -34
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md027_multiple_spaces_blockquote.rs +35 -9
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md028_no_blanks_blockquote.rs +19 -10
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md029_ordered_list_prefix.rs +5 -3
- rumdl-0.0.68/src/rules/md030_list_marker_space.rs +420 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md031_blanks_around_fences.rs +67 -35
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md032_blanks_around_lists.rs +33 -17
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md033_no_inline_html.rs +74 -33
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md034_no_bare_urls.rs +138 -19
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md035_hr_style.rs +12 -7
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md036_no_emphasis_only_first.rs +34 -10
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md037_spaces_around_emphasis.rs +8 -3
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md038_no_space_in_code.rs +3 -1
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md039_no_space_in_links.rs +11 -9
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md040_fenced_code_language.rs +38 -29
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md041_first_line_heading.rs +13 -4
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md042_no_empty_links.rs +9 -3
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md043_required_headings.rs +35 -20
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md044_proper_names.rs +8 -5
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md045_no_alt_text.rs +18 -11
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md046_code_block_style.rs +53 -32
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md047_single_trailing_newline.rs +31 -12
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md048_code_fence_style.rs +39 -21
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md049_emphasis_style.rs +93 -61
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md050_strong_style.rs +16 -10
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md051_link_fragments.rs +13 -5
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md052_reference_links_images.rs +21 -8
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md053_link_image_reference_definitions.rs +66 -38
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md054_link_image_style.rs +69 -37
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md055_table_pipe_style.rs +12 -6
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md056_table_column_count.rs +12 -6
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md057_existing_relative_links.rs +7 -4
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/md058_blanks_around_tables.rs +23 -17
- {rumdl-0.0.66 → rumdl-0.0.68}/src/utils/range_utils.rs +120 -0
- rumdl-0.0.68/tests/character_ranges/additional_tests.rs +101 -0
- rumdl-0.0.68/tests/character_ranges/basic_tests.rs +129 -0
- rumdl-0.0.68/tests/character_ranges/comprehensive_tests.rs +104 -0
- rumdl-0.0.68/tests/character_ranges/extended_tests.rs +257 -0
- rumdl-0.0.68/tests/character_ranges/mod.rs +330 -0
- rumdl-0.0.68/tests/character_ranges/unicode_utils.rs +293 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/lib.rs +1 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/lsp_tests.rs +14 -2
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md010_test.rs +6 -6
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md014_test.rs +7 -5
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md019_test.rs +1 -1
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md020_test.rs +3 -3
- rumdl-0.0.68/tests/rules/md030_test.rs +468 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md033_test.rs +102 -8
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md034_test.rs +1 -1
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md036_test.rs +22 -2
- rumdl-0.0.66/src/rules/md030_list_marker_space.rs +0 -230
- rumdl-0.0.66/tests/rules/md030_test.rs +0 -194
- {rumdl-0.0.66 → rumdl-0.0.68}/.rumdl.toml +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/MANIFEST.in +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/Makefile +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/README.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/assets/logo.png +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/benches/range_performance.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/benches/range_utils_benchmark.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/benches/rule_performance.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/RULES.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md001.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md002.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md003.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md004.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md005.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md006.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md007.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md009.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md010.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md011.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md012.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md013.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md014.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md018.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md019.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md020.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md021.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md022.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md023.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md024.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md025.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md026.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md027.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md028.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md029.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md030.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md031.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md032.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md034.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md035.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md037.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md038.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md039.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md040.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md041.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md042.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md043.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md044.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md045.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md046.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md047.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md048.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md049.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md050.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md051.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md052.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md053.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md054.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md055.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md056.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md057.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/docs/md058.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/issues/plan-rule-parity-with-markdownlint.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/parity_check.py +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/pyproject.toml +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/python/MANIFEST.in +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/python/PYTHON-README.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/python/rumdl/__init__.py +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/python/rumdl/__main__.py +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/python/rumdl/py.typed +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/rumdl.toml.example +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/config.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/init.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/lib.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/lint_context.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/lsp/mod.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/lsp/server.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/markdownlint_config.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/profiling.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/python.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/blockquote_utils.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/code_block_utils.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/code_fence_utils.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/emphasis_style.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/front_matter_utils.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/heading_utils.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/list_utils.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/mod.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/rules/strong_style.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/utils/ast_utils.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/utils/code_block_utils.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/utils/document_structure.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/utils/early_returns.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/utils/element_cache.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/utils/markdown_elements.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/utils/mod.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/utils/regex_cache.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/src/utils/string_interner.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/advanced_integration_tests.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/cli_duplication_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/cli_integration_tests.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/commonmark_compliance_tests.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/comprehensive_integration_tests.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/config_application_tests.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/config_tests.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/init_command_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/init_tests.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/integration_tests.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/json_output_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/lsp_integration_tests.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/markdownlint_cli_integration.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/markdownlint_config_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/md030_edge_cases.md +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/output_format_tests.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/perf_check.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/pyproject_config_tests.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md001_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md001_unicode_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md002_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md003_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md004_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md005_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md006_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md006_unicode_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md007_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md009_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md011_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md012_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md013_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md018_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md021_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md022_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md023_extended_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md023_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md024_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md025_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md026_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md027_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md028_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md029_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md031_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md032_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md033_extended_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md035_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md037_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md038_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md039_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md040_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md041_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md042_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md043_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md044_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md045_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md046_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md047_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md048_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md049_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md050_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md051_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md052_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md053_additional_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md053_proptest.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md053_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md054_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md054_unicode_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md055_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md056_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md057_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/md058_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/rules/mod.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/utils/blockquote_utils_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/utils/code_block_utils_extended_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/utils/code_block_utils_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/utils/core_utils_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/utils/front_matter_utils_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/utils/line_index_test.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/utils/mod.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/utils_markdown_edge_cases.rs +0 -0
- {rumdl-0.0.66 → rumdl-0.0.68}/tests/utils_tests.rs +0 -0
|
@@ -1704,7 +1704,7 @@ dependencies = [
|
|
|
1704
1704
|
|
|
1705
1705
|
[[package]]
|
|
1706
1706
|
name = "rumdl"
|
|
1707
|
-
version = "0.0.
|
|
1707
|
+
version = "0.0.68"
|
|
1708
1708
|
dependencies = [
|
|
1709
1709
|
"anyhow",
|
|
1710
1710
|
"assert_cmd",
|
|
@@ -1748,7 +1748,11 @@ dependencies = [
|
|
|
1748
1748
|
"tower 0.5.2",
|
|
1749
1749
|
"tower-lsp",
|
|
1750
1750
|
"tower-service",
|
|
1751
|
+
"unicode-bidi",
|
|
1752
|
+
"unicode-blocks",
|
|
1751
1753
|
"unicode-normalization",
|
|
1754
|
+
"unicode-segmentation",
|
|
1755
|
+
"unicode-width",
|
|
1752
1756
|
"url",
|
|
1753
1757
|
"walkdir",
|
|
1754
1758
|
]
|
|
@@ -2295,6 +2299,18 @@ version = "0.1.4"
|
|
|
2295
2299
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2296
2300
|
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
|
2297
2301
|
|
|
2302
|
+
[[package]]
|
|
2303
|
+
name = "unicode-bidi"
|
|
2304
|
+
version = "0.3.18"
|
|
2305
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2306
|
+
checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
|
|
2307
|
+
|
|
2308
|
+
[[package]]
|
|
2309
|
+
name = "unicode-blocks"
|
|
2310
|
+
version = "0.1.9"
|
|
2311
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2312
|
+
checksum = "6b12e05d9e06373163a9bb6bb8c263c261b396643a99445fe6b9811fd376581b"
|
|
2313
|
+
|
|
2298
2314
|
[[package]]
|
|
2299
2315
|
name = "unicode-id"
|
|
2300
2316
|
version = "0.3.5"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "rumdl"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.68"
|
|
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>"]
|
|
@@ -81,6 +81,10 @@ proptest = "1.6.0"
|
|
|
81
81
|
criterion = { version = "0.5", features = ["html_reports"] }
|
|
82
82
|
rand = "0.9.1"
|
|
83
83
|
pretty_assertions = "1.4"
|
|
84
|
+
unicode-segmentation = "1.12"
|
|
85
|
+
unicode-width = "0.2"
|
|
86
|
+
unicode-blocks = "0.1"
|
|
87
|
+
unicode-bidi = "0.3"
|
|
84
88
|
|
|
85
89
|
[[bench]]
|
|
86
90
|
name = "rule_performance"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Description
|
|
4
4
|
|
|
5
|
-
This rule disallows the use of inline HTML in Markdown documents. Using inline HTML can reduce the
|
|
5
|
+
This rule disallows the use of inline HTML in Markdown documents. Using inline HTML can reduce the
|
|
6
6
|
portability and compatibility of Markdown content, as some platforms may restrict or sanitize HTML.
|
|
7
7
|
|
|
8
8
|
Pure Markdown is more portable and can be safely rendered in a variety of environments.
|
|
@@ -26,6 +26,12 @@ This is a paragraph with **bold** and *italic* text.
|
|
|
26
26
|
[Link text](https://example.com)
|
|
27
27
|
|
|
28
28
|

|
|
29
|
+
|
|
30
|
+
Visit <https://example.com> for more information.
|
|
31
|
+
|
|
32
|
+
Contact us at <mailto:support@example.com>
|
|
33
|
+
|
|
34
|
+
Download from <ftp://files.example.com/archive.zip>
|
|
29
35
|
```
|
|
30
36
|
|
|
31
37
|
### Invalid
|
|
@@ -87,6 +93,8 @@ Example configuration:
|
|
|
87
93
|
- This rule checks for any HTML tags in the Markdown content
|
|
88
94
|
- It does not apply to code blocks or HTML comments
|
|
89
95
|
- HTML comments (e.g., `<!-- rumdl-disable MD033 -->`, `<!-- rumdl-enable MD033 -->`, or any `<!-- comment -->`) are specifically excluded from this rule
|
|
96
|
+
- URLs in angle brackets (e.g., `<https://example.com>`, `<ftp://files.example.com>`, `<mailto:user@example.com>`) are excluded as they are valid Markdown autolinks, not HTML
|
|
97
|
+
- Email addresses in angle brackets (e.g., `<user@example.com>`) are excluded as they are valid Markdown autolinks
|
|
90
98
|
- The rule can be configured to allow specific HTML tags when necessary
|
|
91
99
|
- Some platforms may still sanitize allowed HTML tags
|
|
92
100
|
|
|
@@ -20,6 +20,14 @@ generation of tables of contents.
|
|
|
20
20
|
## This is another proper heading
|
|
21
21
|
|
|
22
22
|
This is a paragraph with **bold** and *italic* text within it.
|
|
23
|
+
|
|
24
|
+
**Table of Contents**
|
|
25
|
+
|
|
26
|
+
*Contents*
|
|
27
|
+
|
|
28
|
+
__TOC__
|
|
29
|
+
|
|
30
|
+
_Index_
|
|
23
31
|
```
|
|
24
32
|
|
|
25
33
|
### Invalid
|
|
@@ -89,6 +97,9 @@ punctuation = ".!?" # Only remove periods, exclamation marks, and question mark
|
|
|
89
97
|
- Both single emphasis (*italic*) and double emphasis (**bold**) are checked
|
|
90
98
|
- Triple emphasis (***bold italic***) is also checked
|
|
91
99
|
- Trailing punctuation (`.`, `:`, `;`, `!`, `?`) is automatically removed when converting to headings
|
|
100
|
+
- Common Table of Contents labels are excluded and will not be flagged:
|
|
101
|
+
- `**Table of Contents**`, `*Contents*`, `__TOC__`, `_Index_`
|
|
102
|
+
- These are considered legitimate uses of emphasis for document structure
|
|
92
103
|
|
|
93
104
|
## Related Rules
|
|
94
105
|
|
|
@@ -37,10 +37,10 @@ pub fn warning_to_diagnostic(warning: &crate::rule::LintWarning) -> Diagnostic {
|
|
|
37
37
|
character: (warning.column.saturating_sub(1)) as u32,
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
-
//
|
|
40
|
+
// Use proper range from warning
|
|
41
41
|
let end_position = Position {
|
|
42
|
-
line:
|
|
43
|
-
character:
|
|
42
|
+
line: (warning.end_line.saturating_sub(1)) as u32,
|
|
43
|
+
character: (warning.end_column.saturating_sub(1)) as u32,
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
let severity = match warning.severity {
|
|
@@ -48,6 +48,15 @@ pub fn warning_to_diagnostic(warning: &crate::rule::LintWarning) -> Diagnostic {
|
|
|
48
48
|
crate::rule::Severity::Warning => DiagnosticSeverity::WARNING,
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
// Create clickable link to rule documentation
|
|
52
|
+
let code_description = warning.rule_name.as_ref().and_then(|rule_name| {
|
|
53
|
+
// Create a link to the rule documentation
|
|
54
|
+
Url::parse(&format!(
|
|
55
|
+
"https://github.com/rvben/rumdl/blob/main/docs/{}.md",
|
|
56
|
+
rule_name.to_lowercase()
|
|
57
|
+
)).ok().map(|href| CodeDescription { href })
|
|
58
|
+
});
|
|
59
|
+
|
|
51
60
|
Diagnostic {
|
|
52
61
|
range: Range {
|
|
53
62
|
start: start_position,
|
|
@@ -59,7 +68,7 @@ pub fn warning_to_diagnostic(warning: &crate::rule::LintWarning) -> Diagnostic {
|
|
|
59
68
|
message: warning.message.clone(),
|
|
60
69
|
related_information: None,
|
|
61
70
|
tags: None,
|
|
62
|
-
code_description
|
|
71
|
+
code_description,
|
|
63
72
|
data: None,
|
|
64
73
|
}
|
|
65
74
|
}
|
|
@@ -6,7 +6,7 @@ use rayon::prelude::*;
|
|
|
6
6
|
use std::collections::HashSet;
|
|
7
7
|
use std::error::Error;
|
|
8
8
|
use std::fs;
|
|
9
|
-
use std::io::{self, Write};
|
|
9
|
+
use std::io::{self, Write, Read};
|
|
10
10
|
use std::path::Path;
|
|
11
11
|
use std::process;
|
|
12
12
|
use std::sync::Arc;
|
|
@@ -220,6 +220,10 @@ struct CheckArgs {
|
|
|
220
220
|
/// Output format: text (default) or json
|
|
221
221
|
#[arg(long, short = 'o', default_value = "text")]
|
|
222
222
|
output: String,
|
|
223
|
+
|
|
224
|
+
/// Read from stdin instead of files
|
|
225
|
+
#[arg(long, help = "Read from stdin instead of files")]
|
|
226
|
+
stdin: bool,
|
|
223
227
|
}
|
|
224
228
|
|
|
225
229
|
// Get a complete set of enabled rules based on CLI options and config
|
|
@@ -1204,6 +1208,7 @@ build-backend = \"setuptools.build_meta\"
|
|
|
1204
1208
|
profile: cli.profile,
|
|
1205
1209
|
quiet: cli.quiet,
|
|
1206
1210
|
output: "text".to_string(),
|
|
1211
|
+
stdin: false,
|
|
1207
1212
|
};
|
|
1208
1213
|
eprintln!("{}: Deprecation warning: Running 'rumdl .' or 'rumdl [PATHS...]' without a subcommand is deprecated and will be removed in a future release. Please use 'rumdl check .' instead.", "[rumdl]".yellow().bold());
|
|
1209
1214
|
run_check(&args, cli.config.as_deref(), cli.no_config);
|
|
@@ -1225,6 +1230,85 @@ build-backend = \"setuptools.build_meta\"
|
|
|
1225
1230
|
}
|
|
1226
1231
|
}
|
|
1227
1232
|
|
|
1233
|
+
/// Process markdown content from stdin
|
|
1234
|
+
fn process_stdin(rules: &[Box<dyn Rule>], args: &CheckArgs) {
|
|
1235
|
+
// Read all content from stdin
|
|
1236
|
+
let mut content = String::new();
|
|
1237
|
+
if let Err(e) = io::stdin().read_to_string(&mut content) {
|
|
1238
|
+
if !args.quiet {
|
|
1239
|
+
eprintln!("Error reading from stdin: {}", e);
|
|
1240
|
+
}
|
|
1241
|
+
process::exit(1);
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// Create a lint context for the stdin content
|
|
1245
|
+
let ctx = LintContext::new(&content);
|
|
1246
|
+
let mut all_warnings = Vec::new();
|
|
1247
|
+
|
|
1248
|
+
// Run all enabled rules on the content
|
|
1249
|
+
for rule in rules {
|
|
1250
|
+
match rule.check(&ctx) {
|
|
1251
|
+
Ok(mut warnings) => {
|
|
1252
|
+
// Set file path to "<stdin>" for all warnings
|
|
1253
|
+
for warning in &mut warnings {
|
|
1254
|
+
// The warnings already have the correct character ranges
|
|
1255
|
+
}
|
|
1256
|
+
all_warnings.extend(warnings);
|
|
1257
|
+
}
|
|
1258
|
+
Err(e) => {
|
|
1259
|
+
if !args.quiet {
|
|
1260
|
+
eprintln!("Error running rule {}: {}", rule.name(), e);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
// Output results
|
|
1267
|
+
if args.output == "json" {
|
|
1268
|
+
// For JSON output, modify warnings to show "<stdin>" as filename
|
|
1269
|
+
let mut json_warnings = Vec::new();
|
|
1270
|
+
for warning in all_warnings {
|
|
1271
|
+
let mut json_warning = serde_json::to_value(&warning).unwrap();
|
|
1272
|
+
if let Some(obj) = json_warning.as_object_mut() {
|
|
1273
|
+
obj.insert("file".to_string(), serde_json::Value::String("<stdin>".to_string()));
|
|
1274
|
+
}
|
|
1275
|
+
json_warnings.push(json_warning);
|
|
1276
|
+
}
|
|
1277
|
+
println!("{}", serde_json::to_string_pretty(&json_warnings).unwrap());
|
|
1278
|
+
} else {
|
|
1279
|
+
// Text output
|
|
1280
|
+
let has_issues = !all_warnings.is_empty();
|
|
1281
|
+
if has_issues {
|
|
1282
|
+
for warning in &all_warnings {
|
|
1283
|
+
println!(
|
|
1284
|
+
"<stdin>:{}:{}: {}: {} [{}]",
|
|
1285
|
+
warning.line,
|
|
1286
|
+
warning.column,
|
|
1287
|
+
match warning.severity {
|
|
1288
|
+
rumdl::rule::Severity::Error => "error".red(),
|
|
1289
|
+
rumdl::rule::Severity::Warning => "warning".yellow(),
|
|
1290
|
+
},
|
|
1291
|
+
warning.message,
|
|
1292
|
+
warning.rule_name.as_deref().unwrap_or("unknown")
|
|
1293
|
+
);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
if !args.quiet {
|
|
1298
|
+
if has_issues {
|
|
1299
|
+
println!("\nFound {} issue(s) in stdin", all_warnings.len());
|
|
1300
|
+
} else {
|
|
1301
|
+
println!("No issues found in stdin");
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
// Exit with error code if issues found
|
|
1306
|
+
if has_issues {
|
|
1307
|
+
process::exit(1);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1228
1312
|
fn run_check(args: &CheckArgs, global_config_path: Option<&str>, no_config: bool) {
|
|
1229
1313
|
// 1. Load sourced config (for provenance and validation)
|
|
1230
1314
|
let sourced = match rumdl_config::SourcedConfig::load_with_discovery(
|
|
@@ -1257,6 +1341,12 @@ fn run_check(args: &CheckArgs, global_config_path: Option<&str>, no_config: bool
|
|
|
1257
1341
|
// Initialize rules with configuration
|
|
1258
1342
|
let enabled_rules = get_enabled_rules_from_checkargs(args, &config);
|
|
1259
1343
|
|
|
1344
|
+
// Handle stdin input
|
|
1345
|
+
if args.stdin {
|
|
1346
|
+
process_stdin(&enabled_rules, args);
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1260
1350
|
// Find all markdown files to check
|
|
1261
1351
|
let file_paths = match find_markdown_files(&args.paths, args, &config) {
|
|
1262
1352
|
Ok(paths) => paths,
|
|
@@ -43,8 +43,10 @@ pub type LintResult = Result<Vec<LintWarning>, LintError>;
|
|
|
43
43
|
#[derive(Debug, PartialEq, Clone, Serialize)]
|
|
44
44
|
pub struct LintWarning {
|
|
45
45
|
pub message: String,
|
|
46
|
-
pub line: usize,
|
|
47
|
-
pub column: usize,
|
|
46
|
+
pub line: usize, // 1-indexed start line
|
|
47
|
+
pub column: usize, // 1-indexed start column
|
|
48
|
+
pub end_line: usize, // 1-indexed end line
|
|
49
|
+
pub end_column: usize, // 1-indexed end column
|
|
48
50
|
pub severity: Severity,
|
|
49
51
|
pub fix: Option<Fix>,
|
|
50
52
|
pub rule_name: Option<&'static str>,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use crate::rule::{Fix, LintError, LintResult, LintWarning, Rule, RuleCategory, Severity};
|
|
2
2
|
use crate::rules::heading_utils::HeadingUtils;
|
|
3
3
|
use crate::utils::document_structure::DocumentStructure;
|
|
4
|
-
use crate::utils::range_utils::LineIndex;
|
|
4
|
+
use crate::utils::range_utils::{LineIndex, calculate_heading_range};
|
|
5
5
|
use crate::HeadingStyle;
|
|
6
6
|
|
|
7
7
|
/// Rule MD001: Heading levels should only increment by one level at a time
|
|
@@ -147,10 +147,23 @@ impl Rule for MD001HeadingIncrement {
|
|
|
147
147
|
let replacement =
|
|
148
148
|
HeadingUtils::convert_heading_style(&heading_text, fixed_level as u32, style);
|
|
149
149
|
|
|
150
|
+
// Calculate precise range: highlight the entire heading
|
|
151
|
+
let line_content = if adjusted_line_num < lines.len() {
|
|
152
|
+
lines[adjusted_line_num]
|
|
153
|
+
} else {
|
|
154
|
+
""
|
|
155
|
+
};
|
|
156
|
+
let (start_line, start_col, end_line, end_col) = calculate_heading_range(
|
|
157
|
+
line_num,
|
|
158
|
+
line_content
|
|
159
|
+
);
|
|
160
|
+
|
|
150
161
|
warnings.push(LintWarning {
|
|
151
162
|
rule_name: Some(self.name()),
|
|
152
|
-
line:
|
|
153
|
-
column:
|
|
163
|
+
line: start_line,
|
|
164
|
+
column: start_col,
|
|
165
|
+
end_line: end_line,
|
|
166
|
+
end_column: end_col,
|
|
154
167
|
message: format!("Heading level should be {} for this level", prev_level + 1),
|
|
155
168
|
severity: Severity::Warning,
|
|
156
169
|
fix: Some(Fix {
|
|
@@ -2,6 +2,7 @@ use crate::rule::Rule;
|
|
|
2
2
|
use crate::rule::{Fix, LintError, LintResult, LintWarning, RuleCategory, Severity};
|
|
3
3
|
use crate::rules::heading_utils::HeadingStyle;
|
|
4
4
|
use crate::utils::document_structure::DocumentStructure;
|
|
5
|
+
use crate::utils::range_utils::calculate_heading_range;
|
|
5
6
|
use lazy_static::lazy_static;
|
|
6
7
|
use regex::Regex;
|
|
7
8
|
use toml;
|
|
@@ -238,10 +239,25 @@ impl Rule for MD002FirstHeadingH1 {
|
|
|
238
239
|
}
|
|
239
240
|
},
|
|
240
241
|
);
|
|
242
|
+
|
|
243
|
+
// Calculate precise range: highlight the entire first heading
|
|
244
|
+
let lines: Vec<&str> = content.lines().collect();
|
|
245
|
+
let line_content = if first_heading_line > 0 && first_heading_line <= lines.len() {
|
|
246
|
+
lines[first_heading_line - 1]
|
|
247
|
+
} else {
|
|
248
|
+
""
|
|
249
|
+
};
|
|
250
|
+
let (start_line, start_col, end_line, end_col) = calculate_heading_range(
|
|
251
|
+
first_heading_line,
|
|
252
|
+
line_content
|
|
253
|
+
);
|
|
254
|
+
|
|
241
255
|
result.push(LintWarning {
|
|
242
256
|
message,
|
|
243
|
-
line:
|
|
244
|
-
column:
|
|
257
|
+
line: start_line,
|
|
258
|
+
column: start_col,
|
|
259
|
+
end_line: end_line,
|
|
260
|
+
end_column: end_col,
|
|
245
261
|
severity: Severity::Warning,
|
|
246
262
|
fix,
|
|
247
263
|
rule_name: Some(self.name()),
|
|
@@ -4,9 +4,9 @@
|
|
|
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::
|
|
7
|
+
use crate::rules::heading_utils::HeadingStyle;
|
|
8
8
|
use crate::utils::document_structure::DocumentStructure;
|
|
9
|
-
use crate::utils::
|
|
9
|
+
use crate::utils::range_utils::calculate_heading_range;
|
|
10
10
|
use lazy_static::lazy_static;
|
|
11
11
|
use regex::Regex;
|
|
12
12
|
use std::str::FromStr;
|
|
@@ -166,143 +166,29 @@ impl Rule for MD003HeadingStyle {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
fn fix(&self, ctx: &crate::lint_context::LintContext) -> Result<String, LintError> {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if content.is_empty() {
|
|
172
|
-
return Ok(String::new());
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Quick check if there are any headings at all
|
|
176
|
-
if !QUICK_HEADING_CHECK.is_match(content) {
|
|
177
|
-
return Ok(content.to_string());
|
|
178
|
-
}
|
|
179
|
-
|
|
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(&""));
|
|
203
|
-
}
|
|
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
|
-
}
|
|
169
|
+
// Get all warnings with their fixes
|
|
170
|
+
let warnings = self.check(ctx)?;
|
|
285
171
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
172
|
+
// If no warnings, return original content
|
|
173
|
+
if warnings.is_empty() {
|
|
174
|
+
return Ok(ctx.content.to_string());
|
|
290
175
|
}
|
|
291
176
|
|
|
292
|
-
//
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
177
|
+
// Collect all fixes and sort by range start (descending) to apply from end to beginning
|
|
178
|
+
let mut fixes: Vec<_> = warnings.iter()
|
|
179
|
+
.filter_map(|w| w.fix.as_ref().map(|f| (f.range.start, f.range.end, &f.replacement)))
|
|
180
|
+
.collect();
|
|
181
|
+
fixes.sort_by(|a, b| b.0.cmp(&a.0));
|
|
182
|
+
|
|
183
|
+
// Apply fixes from end to beginning to preserve byte offsets
|
|
184
|
+
let mut result = ctx.content.to_string();
|
|
185
|
+
for (start, end, replacement) in fixes {
|
|
186
|
+
if start < result.len() && end <= result.len() && start <= end {
|
|
187
|
+
result.replace_range(start..end, replacement);
|
|
296
188
|
}
|
|
297
|
-
fixed_content.push_str(lines.get(i).unwrap_or(&""));
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Preserve trailing newline
|
|
301
|
-
if content.ends_with('\n') && !fixed_content.ends_with('\n') {
|
|
302
|
-
fixed_content.push('\n');
|
|
303
189
|
}
|
|
304
190
|
|
|
305
|
-
Ok(
|
|
191
|
+
Ok(result)
|
|
306
192
|
}
|
|
307
193
|
|
|
308
194
|
fn check_with_structure(
|
|
@@ -383,16 +269,88 @@ impl Rule for MD003HeadingStyle {
|
|
|
383
269
|
};
|
|
384
270
|
|
|
385
271
|
if style != expected_style {
|
|
272
|
+
// Generate fix for this heading
|
|
273
|
+
let fix = {
|
|
274
|
+
use crate::rules::heading_utils::HeadingUtils;
|
|
275
|
+
|
|
276
|
+
// Get the text content from the heading
|
|
277
|
+
let text_content = if next_line_idx < lines.len() &&
|
|
278
|
+
(lines[next_line_idx].trim_start().starts_with('=') ||
|
|
279
|
+
lines[next_line_idx].trim_start().starts_with('-')) {
|
|
280
|
+
// Setext heading
|
|
281
|
+
current_line.to_string()
|
|
282
|
+
} else {
|
|
283
|
+
// ATX heading
|
|
284
|
+
HeadingUtils::get_heading_text(current_line).unwrap_or_default()
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Get indentation
|
|
288
|
+
let indentation = current_line.chars()
|
|
289
|
+
.take_while(|c| c.is_whitespace())
|
|
290
|
+
.collect::<String>();
|
|
291
|
+
|
|
292
|
+
// Convert heading to target style
|
|
293
|
+
let converted_heading = HeadingUtils::convert_heading_style(
|
|
294
|
+
&format!("{}{}", indentation, text_content.trim()),
|
|
295
|
+
level as u32,
|
|
296
|
+
expected_style,
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
// Calculate the correct range for the heading
|
|
300
|
+
let line_index = crate::utils::range_utils::LineIndex::new(ctx.content.to_string());
|
|
301
|
+
let range = if next_line_idx < lines.len() &&
|
|
302
|
+
(lines[next_line_idx].trim_start().starts_with('=') ||
|
|
303
|
+
lines[next_line_idx].trim_start().starts_with('-')) {
|
|
304
|
+
// Setext heading spans two lines
|
|
305
|
+
let start_byte = line_index.line_col_to_byte_range(line_num, 1).start;
|
|
306
|
+
let end_byte = if line_num + 1 < lines.len() {
|
|
307
|
+
line_index.line_col_to_byte_range(line_num + 2, 1).start - 1
|
|
308
|
+
} else {
|
|
309
|
+
ctx.content.len()
|
|
310
|
+
};
|
|
311
|
+
start_byte..end_byte
|
|
312
|
+
} else {
|
|
313
|
+
// ATX heading is single line
|
|
314
|
+
let start_byte = line_index.line_col_to_byte_range(line_num, 1).start;
|
|
315
|
+
let end_byte = if line_num < lines.len() {
|
|
316
|
+
line_index.line_col_to_byte_range(line_num + 1, 1).start - 1
|
|
317
|
+
} else {
|
|
318
|
+
ctx.content.len()
|
|
319
|
+
};
|
|
320
|
+
start_byte..end_byte
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
Some(crate::rule::Fix {
|
|
324
|
+
range,
|
|
325
|
+
replacement: converted_heading,
|
|
326
|
+
})
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// Calculate precise character range for the heading marker
|
|
330
|
+
let (start_line, start_col, end_line, end_col) = if style == HeadingStyle::Setext1 || style == HeadingStyle::Setext2 {
|
|
331
|
+
// For Setext headings, highlight the underline
|
|
332
|
+
if next_line_idx < lines.len() {
|
|
333
|
+
calculate_heading_range(line_num + 1, lines[next_line_idx])
|
|
334
|
+
} else {
|
|
335
|
+
calculate_heading_range(line_num, current_line)
|
|
336
|
+
}
|
|
337
|
+
} else {
|
|
338
|
+
// For ATX headings, highlight the hash markers
|
|
339
|
+
calculate_heading_range(line_num, current_line)
|
|
340
|
+
};
|
|
341
|
+
|
|
386
342
|
result.push(LintWarning {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
343
|
+
rule_name: Some(self.name()),
|
|
344
|
+
line: start_line,
|
|
345
|
+
column: start_col,
|
|
346
|
+
end_line: end_line,
|
|
347
|
+
end_column: end_col,
|
|
348
|
+
message: format!(
|
|
349
|
+
"Heading style should be {:?}, found {:?}",
|
|
392
350
|
expected_style, style
|
|
393
351
|
),
|
|
394
352
|
severity: Severity::Warning,
|
|
395
|
-
fix
|
|
353
|
+
fix,
|
|
396
354
|
});
|
|
397
355
|
}
|
|
398
356
|
}
|
|
@@ -168,9 +168,11 @@ impl Rule for MD004UnorderedListStyle {
|
|
|
168
168
|
if marker != first {
|
|
169
169
|
let (line, col) = ctx.offset_to_line_col(offset);
|
|
170
170
|
warnings.push(LintWarning {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
line,
|
|
172
|
+
column: col,
|
|
173
|
+
end_line: line,
|
|
174
|
+
end_column: col + 1,
|
|
175
|
+
message: format!("marker '{}' does not match expected style '{}'", marker, first),
|
|
174
176
|
severity: Severity::Warning,
|
|
175
177
|
rule_name: Some(self.name()),
|
|
176
178
|
fix: Some(Fix {
|
|
@@ -195,9 +197,11 @@ impl Rule for MD004UnorderedListStyle {
|
|
|
195
197
|
if marker != target_marker {
|
|
196
198
|
let (line, col) = ctx.offset_to_line_col(offset);
|
|
197
199
|
warnings.push(LintWarning {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
200
|
+
line,
|
|
201
|
+
column: col,
|
|
202
|
+
end_line: line,
|
|
203
|
+
end_column: col + 1,
|
|
204
|
+
message: format!("marker '{}' does not match expected style '{}'", marker, target_marker),
|
|
201
205
|
severity: Severity::Warning,
|
|
202
206
|
rule_name: Some(self.name()),
|
|
203
207
|
fix: Some(Fix {
|