rumdl 0.0.76__tar.gz → 0.0.78__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.76 → rumdl-0.0.78}/Cargo.lock +13 -19
- {rumdl-0.0.76 → rumdl-0.0.78}/Cargo.toml +21 -1
- {rumdl-0.0.76 → rumdl-0.0.78}/PKG-INFO +1 -1
- {rumdl-0.0.76 → rumdl-0.0.78}/src/main.rs +39 -1
- rumdl-0.0.78/src/rules/md005_list_indent.rs +451 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md006_start_bullets.rs +8 -4
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md007_ul_indent.rs +34 -52
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md023_heading_start_left.rs +8 -87
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md026_no_trailing_punctuation.rs +113 -139
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md027_multiple_spaces_blockquote.rs +6 -3
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md037_spaces_around_emphasis.rs +140 -152
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md039_no_space_in_links.rs +170 -150
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md051_link_fragments.rs +122 -101
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/character_ranges/additional_tests.rs +2 -2
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/character_ranges/extended_tests.rs +3 -3
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md007_test.rs +3 -3
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/vscode_extension_fixes.rs +44 -5
- rumdl-0.0.76/src/rules/md005_list_indent.rs +0 -850
- {rumdl-0.0.76 → rumdl-0.0.78}/.rumdl.toml +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/MANIFEST.in +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/Makefile +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/README.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/assets/logo.png +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/benches/fix_performance.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/benches/range_performance.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/benches/range_utils_benchmark.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/benches/rule_performance.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/benches/simple_fix_bench.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/RULES.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md001.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md002.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md003.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md004.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md005.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md006.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md007.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md009.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md010.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md011.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md012.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md013.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md014.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md018.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md019.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md020.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md021.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md022.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md023.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md024.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md025.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md026.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md027.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md028.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md029.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md030.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md031.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md032.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md033.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md034.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md035.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md036.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md037.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md038.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md039.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md040.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md041.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md042.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md043.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md044.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md045.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md046.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md047.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md048.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md049.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md050.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md051.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md052.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md053.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md054.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md055.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md056.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md057.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/docs/md058.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/issues/plan-rule-parity-with-markdownlint.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/parity_check.py +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/pyproject.toml +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/python/MANIFEST.in +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/python/PYTHON-README.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/python/rumdl/__init__.py +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/python/rumdl/__main__.py +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/python/rumdl/py.typed +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/rumdl.toml.example +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/config.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/init.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/lib.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/lint_context.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/lsp/mod.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/lsp/server.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/lsp/types.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/markdownlint_config.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/parallel.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/performance.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/profiling.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/python.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rule.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/blockquote_utils.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/code_block_utils.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/code_fence_utils.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/emphasis_style.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/front_matter_utils.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/heading_utils.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/list_utils.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md001_heading_increment.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md002_first_heading_h1.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md003_heading_style.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md004_unordered_list_style.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md009_trailing_spaces.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md010_no_hard_tabs.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md011_no_reversed_links.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md012_no_multiple_blanks.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md013_line_length.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md014_commands_show_output.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md018_no_missing_space_atx.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md019_no_multiple_space_atx.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md020_no_missing_space_closed_atx.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md021_no_multiple_space_closed_atx.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md022_blanks_around_headings.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md024_no_duplicate_heading.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md025_single_title.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md028_no_blanks_blockquote.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md029_ordered_list_prefix.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md030_list_marker_space.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md031_blanks_around_fences.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md032_blanks_around_lists.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md033_no_inline_html.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md034_no_bare_urls.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md035_hr_style.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md036_no_emphasis_only_first.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md038_no_space_in_code.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md040_fenced_code_language.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md041_first_line_heading.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md042_no_empty_links.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md043_required_headings.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md044_proper_names.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md045_no_alt_text.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md046_code_block_style.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md047_single_trailing_newline.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md048_code_fence_style.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md049_emphasis_style.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md050_strong_style.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md052_reference_links_images.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md053_link_image_reference_definitions.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md054_link_image_style.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md055_table_pipe_style.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md056_table_column_count.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md057_existing_relative_links.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/md058_blanks_around_tables.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/mod.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/rules/strong_style.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/utils/ast_utils.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/utils/code_block_utils.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/utils/document_structure.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/utils/early_returns.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/utils/element_cache.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/utils/markdown_elements.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/utils/mod.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/utils/range_utils.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/utils/regex_cache.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/utils/string_interner.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/src/utils/table_utils.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/advanced_integration_tests.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/character_ranges/basic_tests.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/character_ranges/comprehensive_tests.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/character_ranges/mod.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/character_ranges/unicode_utils.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/cli_duplication_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/cli_integration_tests.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/commonmark_compliance_tests.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/comprehensive_integration_tests.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/config_application_tests.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/config_tests.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/init_command_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/init_tests.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/integration_tests.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/json_output_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/lib.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/lsp_integration_tests.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/lsp_tests.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/markdownlint_cli_integration.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/markdownlint_config_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/md030_edge_cases.md +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/output_format_tests.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/perf_check.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/pyproject_config_tests.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md001_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md001_unicode_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md002_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md003_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md004_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md005_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md006_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md006_unicode_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md009_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md010_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md011_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md012_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md013_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md014_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md018_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md019_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md020_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md021_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md022_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md023_extended_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md023_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md024_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md025_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md026_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md027_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md028_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md029_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md030_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md031_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md032_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md033_extended_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md033_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md034_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md035_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md036_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md037_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md038_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md039_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md040_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md041_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md042_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md043_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md044_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md045_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md046_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md047_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md048_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md049_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md050_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md051_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md052_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md053_additional_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md053_proptest.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md053_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md054_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md054_unicode_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md055_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md056_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md057_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/md058_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/rules/mod.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/utils/blockquote_utils_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/utils/code_block_utils_extended_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/utils/code_block_utils_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/utils/core_utils_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/utils/front_matter_utils_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/utils/line_index_test.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/utils/mod.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/utils_markdown_edge_cases.rs +0 -0
- {rumdl-0.0.76 → rumdl-0.0.78}/tests/utils_tests.rs +0 -0
|
@@ -244,9 +244,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|
|
244
244
|
|
|
245
245
|
[[package]]
|
|
246
246
|
name = "cc"
|
|
247
|
-
version = "1.2.
|
|
247
|
+
version = "1.2.25"
|
|
248
248
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
249
|
-
checksum = "
|
|
249
|
+
checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951"
|
|
250
250
|
dependencies = [
|
|
251
251
|
"shlex",
|
|
252
252
|
]
|
|
@@ -869,12 +869,6 @@ version = "0.5.0"
|
|
|
869
869
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
870
870
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
871
871
|
|
|
872
|
-
[[package]]
|
|
873
|
-
name = "hermit-abi"
|
|
874
|
-
version = "0.3.9"
|
|
875
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
876
|
-
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
|
877
|
-
|
|
878
872
|
[[package]]
|
|
879
873
|
name = "hermit-abi"
|
|
880
874
|
version = "0.5.1"
|
|
@@ -1069,7 +1063,7 @@ version = "0.4.16"
|
|
|
1069
1063
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1070
1064
|
checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
|
|
1071
1065
|
dependencies = [
|
|
1072
|
-
"hermit-abi
|
|
1066
|
+
"hermit-abi",
|
|
1073
1067
|
"libc",
|
|
1074
1068
|
"windows-sys 0.59.0",
|
|
1075
1069
|
]
|
|
@@ -1175,9 +1169,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
|
|
|
1175
1169
|
|
|
1176
1170
|
[[package]]
|
|
1177
1171
|
name = "lock_api"
|
|
1178
|
-
version = "0.4.
|
|
1172
|
+
version = "0.4.13"
|
|
1179
1173
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1180
|
-
checksum = "
|
|
1174
|
+
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
|
1181
1175
|
dependencies = [
|
|
1182
1176
|
"autocfg",
|
|
1183
1177
|
"scopeguard",
|
|
@@ -1272,11 +1266,11 @@ dependencies = [
|
|
|
1272
1266
|
|
|
1273
1267
|
[[package]]
|
|
1274
1268
|
name = "num_cpus"
|
|
1275
|
-
version = "1.
|
|
1269
|
+
version = "1.17.0"
|
|
1276
1270
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1277
|
-
checksum = "
|
|
1271
|
+
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
|
|
1278
1272
|
dependencies = [
|
|
1279
|
-
"hermit-abi
|
|
1273
|
+
"hermit-abi",
|
|
1280
1274
|
"libc",
|
|
1281
1275
|
]
|
|
1282
1276
|
|
|
@@ -1325,9 +1319,9 @@ dependencies = [
|
|
|
1325
1319
|
|
|
1326
1320
|
[[package]]
|
|
1327
1321
|
name = "parking_lot"
|
|
1328
|
-
version = "0.12.
|
|
1322
|
+
version = "0.12.4"
|
|
1329
1323
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1330
|
-
checksum = "
|
|
1324
|
+
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
|
1331
1325
|
dependencies = [
|
|
1332
1326
|
"lock_api",
|
|
1333
1327
|
"parking_lot_core",
|
|
@@ -1335,9 +1329,9 @@ dependencies = [
|
|
|
1335
1329
|
|
|
1336
1330
|
[[package]]
|
|
1337
1331
|
name = "parking_lot_core"
|
|
1338
|
-
version = "0.9.
|
|
1332
|
+
version = "0.9.11"
|
|
1339
1333
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1340
|
-
checksum = "
|
|
1334
|
+
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
|
1341
1335
|
dependencies = [
|
|
1342
1336
|
"cfg-if",
|
|
1343
1337
|
"libc",
|
|
@@ -1789,7 +1783,7 @@ dependencies = [
|
|
|
1789
1783
|
|
|
1790
1784
|
[[package]]
|
|
1791
1785
|
name = "rumdl"
|
|
1792
|
-
version = "0.0.
|
|
1786
|
+
version = "0.0.78"
|
|
1793
1787
|
dependencies = [
|
|
1794
1788
|
"anyhow",
|
|
1795
1789
|
"assert_cmd",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "rumdl"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.78"
|
|
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>"]
|
|
@@ -21,6 +21,26 @@ crate-type = ["cdylib", "rlib"]
|
|
|
21
21
|
name = "rumdl"
|
|
22
22
|
path = "src/main.rs"
|
|
23
23
|
|
|
24
|
+
[[bench]]
|
|
25
|
+
name = "rule_performance"
|
|
26
|
+
harness = false
|
|
27
|
+
|
|
28
|
+
[[bench]]
|
|
29
|
+
name = "fix_performance"
|
|
30
|
+
harness = false
|
|
31
|
+
|
|
32
|
+
[[bench]]
|
|
33
|
+
name = "range_performance"
|
|
34
|
+
harness = false
|
|
35
|
+
|
|
36
|
+
[[bench]]
|
|
37
|
+
name = "simple_fix_bench"
|
|
38
|
+
harness = false
|
|
39
|
+
|
|
40
|
+
[[bench]]
|
|
41
|
+
name = "range_utils_benchmark"
|
|
42
|
+
harness = false
|
|
43
|
+
|
|
24
44
|
[dependencies]
|
|
25
45
|
clap = { version = "4.5", features = ["derive"] }
|
|
26
46
|
colored = "3.0.0"
|
|
@@ -164,6 +164,8 @@ enum Commands {
|
|
|
164
164
|
enum ConfigSubcommand {
|
|
165
165
|
/// Query a specific config key (e.g. global.exclude or MD013.line_length)
|
|
166
166
|
Get { key: String },
|
|
167
|
+
/// Show the absolute path of the configuration file that was loaded
|
|
168
|
+
File,
|
|
167
169
|
}
|
|
168
170
|
|
|
169
171
|
#[derive(Args, Debug)]
|
|
@@ -951,7 +953,7 @@ build-backend = \"setuptools.build_meta\"
|
|
|
951
953
|
defaults,
|
|
952
954
|
output,
|
|
953
955
|
}) => {
|
|
954
|
-
// Handle
|
|
956
|
+
// Handle config subcommands
|
|
955
957
|
if let Some(ConfigSubcommand::Get { key }) = subcmd {
|
|
956
958
|
if let Some((section_part, field_part)) = key.split_once('.') {
|
|
957
959
|
// 1. Load the full SourcedConfig once
|
|
@@ -1096,6 +1098,42 @@ build-backend = \"setuptools.build_meta\"
|
|
|
1096
1098
|
std::process::exit(1);
|
|
1097
1099
|
}
|
|
1098
1100
|
}
|
|
1101
|
+
// Handle 'config file' subcommand for showing config file path
|
|
1102
|
+
else if let Some(ConfigSubcommand::File) = subcmd {
|
|
1103
|
+
let sourced = match rumdl_config::SourcedConfig::load_with_discovery(
|
|
1104
|
+
cli.config.as_deref(),
|
|
1105
|
+
None,
|
|
1106
|
+
cli.no_config,
|
|
1107
|
+
) {
|
|
1108
|
+
Ok(s) => s,
|
|
1109
|
+
Err(e) => {
|
|
1110
|
+
eprintln!("{}: {}", "Config error".red().bold(), e);
|
|
1111
|
+
std::process::exit(1);
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1114
|
+
|
|
1115
|
+
if sourced.loaded_files.is_empty() {
|
|
1116
|
+
if cli.no_config {
|
|
1117
|
+
println!("No configuration file loaded (--no-config specified)");
|
|
1118
|
+
} else {
|
|
1119
|
+
println!("No configuration file found (using defaults)");
|
|
1120
|
+
}
|
|
1121
|
+
} else {
|
|
1122
|
+
// Convert relative paths to absolute paths
|
|
1123
|
+
for file_path in &sourced.loaded_files {
|
|
1124
|
+
match std::fs::canonicalize(file_path) {
|
|
1125
|
+
Ok(absolute_path) => {
|
|
1126
|
+
println!("{}", absolute_path.display());
|
|
1127
|
+
}
|
|
1128
|
+
Err(_) => {
|
|
1129
|
+
// If canonicalize fails, it might be a file that doesn't exist anymore
|
|
1130
|
+
// or a relative path that can't be resolved. Just print as-is.
|
|
1131
|
+
println!("{}", file_path);
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1099
1137
|
// --- Fallthrough logic for `rumdl config` (no subcommand) ---
|
|
1100
1138
|
// This code now runs ONLY if `subcmd` is None
|
|
1101
1139
|
else {
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
//!
|
|
2
|
+
//! Rule MD005: Inconsistent indentation for list items at the same level
|
|
3
|
+
//!
|
|
4
|
+
//! See [docs/md005.md](../../docs/md005.md) for full documentation, configuration, and examples.
|
|
5
|
+
|
|
6
|
+
use crate::utils::range_utils::{calculate_match_range, LineIndex};
|
|
7
|
+
|
|
8
|
+
use crate::rule::{Fix, LintError, LintResult, LintWarning, Rule, RuleCategory, Severity};
|
|
9
|
+
use crate::utils::document_structure::DocumentStructure;
|
|
10
|
+
use lazy_static::lazy_static;
|
|
11
|
+
use regex::Regex;
|
|
12
|
+
use std::collections::HashMap;
|
|
13
|
+
use toml;
|
|
14
|
+
|
|
15
|
+
lazy_static! {
|
|
16
|
+
static ref LIST_MARKER_REGEX: Regex = Regex::new(r"^\d+[.)]").unwrap();
|
|
17
|
+
// Pre-compiled pattern for quick list detection
|
|
18
|
+
static ref QUICK_LIST_CHECK: Regex = Regex::new(r"^( *)([*+\-]|\d+[.)])").unwrap();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Rule MD005: Inconsistent indentation for list items at the same level
|
|
22
|
+
#[derive(Clone)]
|
|
23
|
+
pub struct MD005ListIndent;
|
|
24
|
+
|
|
25
|
+
impl MD005ListIndent {
|
|
26
|
+
#[inline]
|
|
27
|
+
fn get_list_marker_info(line: &str) -> Option<(usize, char, usize)> {
|
|
28
|
+
// Early return for empty or whitespace-only lines
|
|
29
|
+
if line.is_empty() || line.trim().is_empty() {
|
|
30
|
+
return None;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let indentation = line.len() - line.trim_start().len();
|
|
34
|
+
let trimmed = line.trim_start();
|
|
35
|
+
|
|
36
|
+
// Fast path check for unordered list markers
|
|
37
|
+
if !trimmed.is_empty() {
|
|
38
|
+
let first_char = trimmed.chars().next().unwrap();
|
|
39
|
+
|
|
40
|
+
// Check for unordered list markers (* - +)
|
|
41
|
+
if (first_char == '*' || first_char == '-' || first_char == '+')
|
|
42
|
+
&& trimmed.len() > 1
|
|
43
|
+
&& trimmed.chars().nth(1).map_or(false, |c| c.is_whitespace())
|
|
44
|
+
{
|
|
45
|
+
return Some((indentation, first_char, 1)); // 1 char marker
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Fast path check for ordered list markers (digits followed by . or ))
|
|
49
|
+
if first_char.is_ascii_digit() {
|
|
50
|
+
if let Some(marker_match) = LIST_MARKER_REGEX.find(trimmed) {
|
|
51
|
+
let marker_char = trimmed.chars().nth(marker_match.end() - 1).unwrap();
|
|
52
|
+
if trimmed.len() > marker_match.end()
|
|
53
|
+
&& trimmed
|
|
54
|
+
.chars()
|
|
55
|
+
.nth(marker_match.end())
|
|
56
|
+
.map_or(false, |c| c.is_whitespace())
|
|
57
|
+
{
|
|
58
|
+
return Some((indentation, marker_char, marker_match.end()));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
None
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#[inline]
|
|
68
|
+
fn is_blank_line(line: &str) -> bool {
|
|
69
|
+
line.trim().is_empty()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Determine the expected indentation for a list item at a specific level
|
|
73
|
+
#[inline]
|
|
74
|
+
fn get_expected_indent(level: usize) -> usize {
|
|
75
|
+
if level == 1 {
|
|
76
|
+
0 // Top level items should be at the start of the line
|
|
77
|
+
} else {
|
|
78
|
+
2 * (level - 1) // Nested items should be indented by 2 spaces per level
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Determine if a line is a continuation of a list item
|
|
83
|
+
#[inline]
|
|
84
|
+
fn is_list_continuation(prev_line: &str, current_line: &str) -> bool {
|
|
85
|
+
// Early return for empty lines
|
|
86
|
+
if current_line.trim().is_empty() {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// If the previous line is a list item and the current line has more indentation
|
|
91
|
+
// but is not a list item itself, it's a continuation
|
|
92
|
+
if let Some((prev_indent, _, _)) = Self::get_list_marker_info(prev_line) {
|
|
93
|
+
let current_indent = current_line.len() - current_line.trim_start().len();
|
|
94
|
+
return current_indent > prev_indent
|
|
95
|
+
&& Self::get_list_marker_info(current_line).is_none();
|
|
96
|
+
}
|
|
97
|
+
false
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/// Optimized check that combines all passes into one
|
|
101
|
+
fn check_optimized(&self, ctx: &crate::lint_context::LintContext) -> LintResult {
|
|
102
|
+
let content = ctx.content;
|
|
103
|
+
|
|
104
|
+
// Early returns for common cases
|
|
105
|
+
if content.is_empty() {
|
|
106
|
+
return Ok(Vec::new());
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Quick check to avoid processing files without list markers
|
|
110
|
+
if !QUICK_LIST_CHECK.is_match(content) {
|
|
111
|
+
return Ok(Vec::new());
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let line_index = LineIndex::new(content.to_string());
|
|
115
|
+
let lines: Vec<&str> = content.lines().collect();
|
|
116
|
+
|
|
117
|
+
// Early return if there are no lines
|
|
118
|
+
if lines.is_empty() {
|
|
119
|
+
return Ok(Vec::new());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let mut warnings = Vec::new();
|
|
123
|
+
|
|
124
|
+
// Pre-compute code blocks once
|
|
125
|
+
let mut in_code_blocks = vec![false; lines.len()];
|
|
126
|
+
let mut in_block = false;
|
|
127
|
+
for (i, line) in lines.iter().enumerate() {
|
|
128
|
+
let trimmed = line.trim();
|
|
129
|
+
if trimmed.starts_with("```") || trimmed.starts_with("~~~") {
|
|
130
|
+
in_block = !in_block;
|
|
131
|
+
}
|
|
132
|
+
in_code_blocks[i] = in_block;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Single pass processing with efficient data structures
|
|
136
|
+
let mut list_items: Vec<(usize, usize, usize)> = Vec::new(); // (line_num, indent, list_id)
|
|
137
|
+
let mut current_list_id = 0;
|
|
138
|
+
let mut in_list = false;
|
|
139
|
+
let mut list_level_maps: HashMap<usize, HashMap<usize, usize>> = HashMap::new(); // list_id -> { indent -> level }
|
|
140
|
+
let mut level_indents: HashMap<(usize, usize), usize> = HashMap::new(); // (list_id, level) -> expected_indent
|
|
141
|
+
|
|
142
|
+
for (line_num, line) in lines.iter().enumerate() {
|
|
143
|
+
// Skip blank lines and code blocks
|
|
144
|
+
if Self::is_blank_line(line) || in_code_blocks[line_num] {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Check if this is a list item
|
|
149
|
+
if let Some((indent, _marker, _)) = Self::get_list_marker_info(line) {
|
|
150
|
+
// Determine if this starts a new list
|
|
151
|
+
let is_new_list = !in_list
|
|
152
|
+
|| indent == 0
|
|
153
|
+
|| (list_items.last().map_or(false, |(_, prev_indent, _)| {
|
|
154
|
+
prev_indent > &0 && indent < prev_indent / 2
|
|
155
|
+
}));
|
|
156
|
+
|
|
157
|
+
if is_new_list {
|
|
158
|
+
current_list_id += 1;
|
|
159
|
+
in_list = true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Determine level for this item
|
|
163
|
+
let level_map = list_level_maps.entry(current_list_id).or_default();
|
|
164
|
+
let level = if level_map.is_empty() {
|
|
165
|
+
level_map.insert(indent, 1);
|
|
166
|
+
level_indents.insert((current_list_id, 1), indent);
|
|
167
|
+
1
|
|
168
|
+
} else {
|
|
169
|
+
// Find appropriate level
|
|
170
|
+
if let Some(&existing_level) = level_map.get(&indent) {
|
|
171
|
+
existing_level
|
|
172
|
+
} else {
|
|
173
|
+
// Find parent level
|
|
174
|
+
let mut level = 1;
|
|
175
|
+
let mut parent_indent = 0;
|
|
176
|
+
|
|
177
|
+
for (&prev_indent, &prev_level) in level_map.iter() {
|
|
178
|
+
if prev_indent < indent && (prev_level >= level || prev_indent > parent_indent) {
|
|
179
|
+
level = prev_level + 1;
|
|
180
|
+
parent_indent = prev_indent;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
level_map.insert(indent, level);
|
|
185
|
+
level
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
list_items.push((line_num, indent, current_list_id));
|
|
190
|
+
|
|
191
|
+
// Check indentation immediately
|
|
192
|
+
let expected_indent = Self::get_expected_indent(level);
|
|
193
|
+
if indent != expected_indent {
|
|
194
|
+
let inconsistent_message = format!(
|
|
195
|
+
"List item indentation is {} {}, expected {} for level {}",
|
|
196
|
+
indent,
|
|
197
|
+
if indent == 1 { "space" } else { "spaces" },
|
|
198
|
+
expected_indent,
|
|
199
|
+
level
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
let (start_line, start_col, end_line, end_col) = if indent > 0 {
|
|
203
|
+
calculate_match_range(line_num + 1, line, 0, indent)
|
|
204
|
+
} else {
|
|
205
|
+
calculate_match_range(line_num + 1, line, 0, 1)
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// Fix range should span from start of line to end of indentation
|
|
209
|
+
let fix_range = if indent > 0 {
|
|
210
|
+
// Replace the current indentation with expected indentation
|
|
211
|
+
let start_byte = line_index.line_col_to_byte_range(line_num + 1, 1).start;
|
|
212
|
+
let end_byte = line_index.line_col_to_byte_range(line_num + 1, indent + 1).start;
|
|
213
|
+
start_byte..end_byte
|
|
214
|
+
} else {
|
|
215
|
+
// For no indentation, insert at start of line
|
|
216
|
+
let byte_pos = line_index.line_col_to_byte_range(line_num + 1, 1).start;
|
|
217
|
+
byte_pos..byte_pos
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// Replacement should be just the corrected indentation
|
|
221
|
+
let replacement = if expected_indent > 0 {
|
|
222
|
+
" ".repeat(expected_indent)
|
|
223
|
+
} else {
|
|
224
|
+
String::new()
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
warnings.push(LintWarning {
|
|
228
|
+
rule_name: Some(self.name()),
|
|
229
|
+
line: start_line,
|
|
230
|
+
column: start_col,
|
|
231
|
+
end_line,
|
|
232
|
+
end_column: end_col,
|
|
233
|
+
message: inconsistent_message,
|
|
234
|
+
severity: Severity::Warning,
|
|
235
|
+
fix: Some(Fix {
|
|
236
|
+
range: fix_range,
|
|
237
|
+
replacement,
|
|
238
|
+
}),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Track level consistency
|
|
243
|
+
let key = (current_list_id, level);
|
|
244
|
+
if let Some(reference_indent) = level_indents.get(&key) {
|
|
245
|
+
if indent != *reference_indent {
|
|
246
|
+
let inconsistent_message = format!(
|
|
247
|
+
"List item indentation is inconsistent with other items at the same level (found: {}, expected: {})",
|
|
248
|
+
indent, reference_indent
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
// Only add if we don't already have a warning for this line
|
|
252
|
+
if !warnings.iter().any(|w| w.line == line_num + 1) {
|
|
253
|
+
let (start_line, start_col, end_line, end_col) = if indent > 0 {
|
|
254
|
+
calculate_match_range(line_num + 1, line, 0, indent)
|
|
255
|
+
} else {
|
|
256
|
+
calculate_match_range(line_num + 1, line, 0, 1)
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// Fix range should span from start of line to end of indentation
|
|
260
|
+
let fix_range = if indent > 0 {
|
|
261
|
+
// Replace the current indentation with expected indentation
|
|
262
|
+
let start_byte = line_index.line_col_to_byte_range(line_num + 1, 1).start;
|
|
263
|
+
let end_byte = line_index.line_col_to_byte_range(line_num + 1, indent + 1).start;
|
|
264
|
+
start_byte..end_byte
|
|
265
|
+
} else {
|
|
266
|
+
// For no indentation, insert at start of line
|
|
267
|
+
let byte_pos = line_index.line_col_to_byte_range(line_num + 1, 1).start;
|
|
268
|
+
byte_pos..byte_pos
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// Replacement should be just the corrected indentation
|
|
272
|
+
let replacement = if *reference_indent > 0 {
|
|
273
|
+
" ".repeat(*reference_indent)
|
|
274
|
+
} else {
|
|
275
|
+
String::new()
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
warnings.push(LintWarning {
|
|
279
|
+
rule_name: Some(self.name()),
|
|
280
|
+
line: start_line,
|
|
281
|
+
column: start_col,
|
|
282
|
+
end_line,
|
|
283
|
+
end_column: end_col,
|
|
284
|
+
message: inconsistent_message,
|
|
285
|
+
severity: Severity::Warning,
|
|
286
|
+
fix: Some(Fix {
|
|
287
|
+
range: fix_range,
|
|
288
|
+
replacement,
|
|
289
|
+
}),
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
} else {
|
|
294
|
+
level_indents.insert(key, indent);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
} else {
|
|
298
|
+
// Check if it's a list continuation
|
|
299
|
+
if list_items.is_empty() || !in_list {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
let (prev_line_num, _, _) = list_items.last().unwrap();
|
|
304
|
+
if !Self::is_list_continuation(lines[*prev_line_num], line) {
|
|
305
|
+
in_list = false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
Ok(warnings)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
impl Default for MD005ListIndent {
|
|
315
|
+
fn default() -> Self {
|
|
316
|
+
Self
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
impl Rule for MD005ListIndent {
|
|
321
|
+
fn name(&self) -> &'static str {
|
|
322
|
+
"MD005"
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
fn description(&self) -> &'static str {
|
|
326
|
+
"List indentation should be consistent"
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
fn check(&self, ctx: &crate::lint_context::LintContext) -> LintResult {
|
|
330
|
+
// Use optimized version
|
|
331
|
+
self.check_optimized(ctx)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
fn fix(&self, ctx: &crate::lint_context::LintContext) -> Result<String, LintError> {
|
|
335
|
+
let warnings = self.check(ctx)?;
|
|
336
|
+
if warnings.is_empty() {
|
|
337
|
+
return Ok(ctx.content.to_string());
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Sort warnings by position (descending) to apply from end to start
|
|
341
|
+
let mut warnings_with_fixes: Vec<_> = warnings
|
|
342
|
+
.into_iter()
|
|
343
|
+
.filter_map(|w| {
|
|
344
|
+
if let Some(fix) = w.fix.clone() {
|
|
345
|
+
Some((w, fix))
|
|
346
|
+
} else {
|
|
347
|
+
None
|
|
348
|
+
}
|
|
349
|
+
})
|
|
350
|
+
.collect();
|
|
351
|
+
warnings_with_fixes.sort_by_key(|(_, fix)| std::cmp::Reverse(fix.range.start));
|
|
352
|
+
|
|
353
|
+
// Apply fixes to content
|
|
354
|
+
let mut content = ctx.content.to_string();
|
|
355
|
+
for (_, fix) in warnings_with_fixes {
|
|
356
|
+
if fix.range.start <= content.len() && fix.range.end <= content.len() {
|
|
357
|
+
content.replace_range(fix.range, &fix.replacement);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
Ok(content)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
fn category(&self) -> RuleCategory {
|
|
365
|
+
RuleCategory::List
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/// Check if this rule should be skipped
|
|
369
|
+
fn should_skip(&self, ctx: &crate::lint_context::LintContext) -> bool {
|
|
370
|
+
let content = ctx.content;
|
|
371
|
+
content.is_empty() || !QUICK_LIST_CHECK.is_match(content)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/// Optimized check using document structure
|
|
375
|
+
fn check_with_structure(
|
|
376
|
+
&self,
|
|
377
|
+
ctx: &crate::lint_context::LintContext,
|
|
378
|
+
structure: &DocumentStructure,
|
|
379
|
+
) -> LintResult {
|
|
380
|
+
// If no lists in structure, return early
|
|
381
|
+
if structure.list_lines.is_empty() {
|
|
382
|
+
return Ok(Vec::new());
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Use optimized check - it's already efficient enough
|
|
386
|
+
self.check_optimized(ctx)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
fn as_any(&self) -> &dyn std::any::Any {
|
|
390
|
+
self
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
|
|
394
|
+
Some(self)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
fn default_config_section(&self) -> Option<(String, toml::Value)> {
|
|
398
|
+
None
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
fn from_config(_config: &crate::config::Config) -> Box<dyn Rule>
|
|
402
|
+
where
|
|
403
|
+
Self: Sized,
|
|
404
|
+
{
|
|
405
|
+
Box::new(MD005ListIndent)
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
impl crate::utils::document_structure::DocumentStructureExtensions for MD005ListIndent {
|
|
410
|
+
fn has_relevant_elements(
|
|
411
|
+
&self,
|
|
412
|
+
_ctx: &crate::lint_context::LintContext,
|
|
413
|
+
doc_structure: &crate::utils::document_structure::DocumentStructure,
|
|
414
|
+
) -> bool {
|
|
415
|
+
!doc_structure.list_lines.is_empty()
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
#[cfg(test)]
|
|
420
|
+
mod tests {
|
|
421
|
+
use super::*;
|
|
422
|
+
use crate::lint_context::LintContext;
|
|
423
|
+
|
|
424
|
+
// ... existing tests ...
|
|
425
|
+
|
|
426
|
+
#[test]
|
|
427
|
+
fn test_with_document_structure() {
|
|
428
|
+
let rule = MD005ListIndent;
|
|
429
|
+
|
|
430
|
+
// Test with consistent list indentation
|
|
431
|
+
let content = "* Item 1\n* Item 2\n * Nested item\n * Another nested item";
|
|
432
|
+
let structure = DocumentStructure::new(content);
|
|
433
|
+
let ctx = LintContext::new(content);
|
|
434
|
+
let result = rule.check_with_structure(&ctx, &structure).unwrap();
|
|
435
|
+
assert!(result.is_empty());
|
|
436
|
+
|
|
437
|
+
// Test with inconsistent list indentation
|
|
438
|
+
let content = "* Item 1\n* Item 2\n * Nested item\n * Another nested item";
|
|
439
|
+
let structure = DocumentStructure::new(content);
|
|
440
|
+
let ctx = LintContext::new(content);
|
|
441
|
+
let result = rule.check_with_structure(&ctx, &structure).unwrap();
|
|
442
|
+
assert!(!result.is_empty()); // Should have at least one warning
|
|
443
|
+
|
|
444
|
+
// Test with different level indentation issues
|
|
445
|
+
let content = "* Item 1\n * Nested item\n * Another nested item with wrong indent";
|
|
446
|
+
let structure = DocumentStructure::new(content);
|
|
447
|
+
let ctx = LintContext::new(content);
|
|
448
|
+
let result = rule.check_with_structure(&ctx, &structure).unwrap();
|
|
449
|
+
assert!(!result.is_empty()); // Should have at least one warning
|
|
450
|
+
}
|
|
451
|
+
}
|
|
@@ -238,9 +238,9 @@ impl Rule for MD006StartBullets {
|
|
|
238
238
|
|
|
239
239
|
if !is_valid {
|
|
240
240
|
// Calculate the precise range for the indentation that needs to be removed
|
|
241
|
-
// For " * Indented bullet", we want to highlight
|
|
241
|
+
// For " * Indented bullet", we want to highlight the indentation, marker, and space after marker " * " (columns 1-4)
|
|
242
242
|
let start_col = 1; // Start from beginning of line
|
|
243
|
-
let end_col = indent +
|
|
243
|
+
let end_col = indent + 3; // Include marker and space after it (indent + 1 for marker + 1 for space + 1 for inclusive range)
|
|
244
244
|
|
|
245
245
|
// For the fix, we need to replace the highlighted part (" *") with just the bullet marker ("* ")
|
|
246
246
|
let line = lines[line_idx];
|
|
@@ -262,8 +262,12 @@ impl Rule for MD006StartBullets {
|
|
|
262
262
|
end_column: end_col,
|
|
263
263
|
message: "List item indentation".to_string(),
|
|
264
264
|
fix: Some(Fix {
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
range: {
|
|
266
|
+
let start_byte = line_index.line_col_to_byte_range(line_num, start_col).start;
|
|
267
|
+
let end_byte = line_index.line_col_to_byte_range(line_num, end_col).start;
|
|
268
|
+
start_byte..end_byte
|
|
269
|
+
},
|
|
270
|
+
replacement,
|
|
267
271
|
}),
|
|
268
272
|
});
|
|
269
273
|
}
|