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.
- {rumdl-0.0.65 → rumdl-0.0.67}/Cargo.lock +13 -5
- {rumdl-0.0.65 → rumdl-0.0.67}/Cargo.toml +2 -2
- {rumdl-0.0.65 → rumdl-0.0.67}/PKG-INFO +1 -1
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md003_heading_style.rs +76 -134
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md006_start_bullets.rs +48 -12
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md007_ul_indent.rs +52 -94
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md013_line_length.rs +204 -40
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md030_list_marker_space.rs +168 -4
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md034_no_bare_urls.rs +60 -33
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md046_code_block_style.rs +142 -3
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md049_emphasis_style.rs +58 -53
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md052_reference_links_images.rs +43 -13
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md053_link_image_reference_definitions.rs +52 -35
- {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/element_cache.rs +195 -21
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md007_test.rs +2 -2
- rumdl-0.0.67/tests/rules/md030_test.rs +468 -0
- rumdl-0.0.65/tests/rules/md030_test.rs +0 -194
- {rumdl-0.0.65 → rumdl-0.0.67}/.rumdl.toml +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/MANIFEST.in +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/Makefile +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/README.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/assets/logo.png +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/benches/range_performance.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/benches/range_utils_benchmark.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/benches/rule_performance.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/RULES.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md001.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md002.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md003.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md004.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md005.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md006.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md007.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md009.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md010.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md011.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md012.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md013.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md014.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md018.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md019.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md020.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md021.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md022.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md023.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md024.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md025.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md026.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md027.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md028.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md029.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md030.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md031.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md032.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md033.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md034.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md035.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md036.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md037.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md038.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md039.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md040.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md041.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md042.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md043.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md044.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md045.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md046.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md047.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md048.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md049.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md050.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md051.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md052.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md053.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md054.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md055.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md056.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md057.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/docs/md058.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/issues/plan-rule-parity-with-markdownlint.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/parity_check.py +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/pyproject.toml +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/python/MANIFEST.in +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/python/PYTHON-README.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/python/rumdl/__init__.py +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/python/rumdl/__main__.py +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/python/rumdl/py.typed +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/rumdl.toml.example +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/config.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/init.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/lib.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/lint_context.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/lsp/mod.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/lsp/server.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/lsp/types.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/main.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/markdownlint_config.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/profiling.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/python.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rule.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/blockquote_utils.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/code_block_utils.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/code_fence_utils.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/emphasis_style.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/front_matter_utils.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/heading_utils.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/list_utils.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md001_heading_increment.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md002_first_heading_h1.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md004_unordered_list_style.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md005_list_indent.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md009_trailing_spaces.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md010_no_hard_tabs.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md011_no_reversed_links.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md012_no_multiple_blanks.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md014_commands_show_output.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md018_no_missing_space_atx.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md019_no_multiple_space_atx.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md020_no_missing_space_closed_atx.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md021_no_multiple_space_closed_atx.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md022_blanks_around_headings.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md023_heading_start_left.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md024_no_duplicate_heading.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md025_single_title.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md026_no_trailing_punctuation.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md027_multiple_spaces_blockquote.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md028_no_blanks_blockquote.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md029_ordered_list_prefix.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md031_blanks_around_fences.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md032_blanks_around_lists.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md033_no_inline_html.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md035_hr_style.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md036_no_emphasis_only_first.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md037_spaces_around_emphasis.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md038_no_space_in_code.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md039_no_space_in_links.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md040_fenced_code_language.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md041_first_line_heading.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md042_no_empty_links.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md043_required_headings.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md044_proper_names.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md045_no_alt_text.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md047_single_trailing_newline.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md048_code_fence_style.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md050_strong_style.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md051_link_fragments.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md054_link_image_style.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md055_table_pipe_style.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md056_table_column_count.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md057_existing_relative_links.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md058_blanks_around_tables.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/mod.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/strong_style.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/ast_utils.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/code_block_utils.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/document_structure.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/early_returns.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/markdown_elements.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/mod.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/range_utils.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/regex_cache.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/string_interner.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/advanced_integration_tests.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/cli_duplication_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/cli_integration_tests.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/commonmark_compliance_tests.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/comprehensive_integration_tests.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/config_application_tests.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/config_tests.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/init_command_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/init_tests.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/integration_tests.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/json_output_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/lib.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/lsp_integration_tests.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/lsp_tests.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/markdownlint_cli_integration.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/markdownlint_config_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/md030_edge_cases.md +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/output_format_tests.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/perf_check.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/pyproject_config_tests.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md001_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md001_unicode_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md002_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md003_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md004_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md005_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md006_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md006_unicode_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md009_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md010_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md011_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md012_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md013_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md014_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md018_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md019_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md020_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md021_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md022_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md023_extended_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md023_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md024_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md025_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md026_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md027_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md028_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md029_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md031_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md032_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md033_extended_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md033_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md034_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md035_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md036_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md037_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md038_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md039_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md040_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md041_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md042_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md043_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md044_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md045_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md046_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md047_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md048_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md049_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md050_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md051_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md052_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md053_additional_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md053_proptest.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md053_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md054_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md054_unicode_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md055_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md056_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md057_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md058_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/mod.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils/blockquote_utils_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils/code_block_utils_extended_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils/code_block_utils_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils/core_utils_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils/front_matter_utils_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils/line_index_test.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils/mod.rs +0 -0
- {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils_markdown_edge_cases.rs +0 -0
- {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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
63
|
+
tower = "0.5.2"
|
|
64
64
|
tower-service = "0.3"
|
|
65
65
|
env_logger = "0.11"
|
|
66
66
|
|
|
@@ -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::
|
|
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
|
-
|
|
170
|
-
|
|
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
|
-
//
|
|
176
|
-
if
|
|
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
|
-
|
|
181
|
-
let mut
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
104
|
-
let
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
//
|
|
128
|
-
let mut
|
|
129
|
-
for (
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
156
|
+
|
|
157
|
+
Ok(result)
|
|
200
158
|
}
|
|
201
159
|
|
|
202
160
|
/// Get the category of this rule for selective processing
|