rumdl 0.0.53__tar.gz → 0.0.54__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.53 → rumdl-0.0.54}/Cargo.lock +1 -1
- {rumdl-0.0.53 → rumdl-0.0.54}/Cargo.toml +1 -1
- {rumdl-0.0.53 → rumdl-0.0.54}/PKG-INFO +1 -1
- rumdl-0.0.54/src/rules/md007_ul_indent.rs +222 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md035_hr_style.rs +28 -39
- {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/element_cache.rs +84 -50
- rumdl-0.0.54/tests/rules/md007_test.rs +127 -0
- rumdl-0.0.54/tests/rules/md035_test.rs +153 -0
- rumdl-0.0.53/src/rules/md007_ul_indent.rs +0 -276
- rumdl-0.0.53/tests/rules/md007_test.rs +0 -69
- rumdl-0.0.53/tests/rules/md035_test.rs +0 -61
- {rumdl-0.0.53 → rumdl-0.0.54}/.rumdl.toml +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/MANIFEST.in +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/Makefile +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/README.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/assets/logo.png +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/benches/range_performance.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/benches/range_utils_benchmark.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/benches/rule_performance.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/RULES.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md001.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md002.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md003.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md004.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md005.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md006.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md007.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md008.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md009.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md010.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md011.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md012.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md013.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md014.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md015.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md018.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md019.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md020.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md021.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md022.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md023.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md024.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md025.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md026.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md027.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md028.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md029.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md030.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md031.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md032.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md033.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md034.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md035.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md036.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md037.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md038.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md039.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md040.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md041.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md042.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md043.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md044.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md045.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md046.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md047.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md048.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md049.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md050.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md051.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md052.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md053.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md054.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md055.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md056.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md057.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/docs/md058.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/issues/plan-rule-parity-with-markdownlint.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/pyproject.toml +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/python/MANIFEST.in +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/python/PYTHON-README.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/python/rumdl/__init__.py +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/python/rumdl/__main__.py +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/python/rumdl/py.typed +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/rumdl.toml.example +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/config.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/init.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/lib.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/lint_context.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/main.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/markdownlint_config.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/profiling.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/python.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rule.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/blockquote_utils.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/code_block_utils.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/code_fence_utils.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/emphasis_style.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/front_matter_utils.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/heading_utils.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/list_utils.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md001_heading_increment.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md002_first_heading_h1.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md003_heading_style.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md004_unordered_list_style.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md005_list_indent.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md006_start_bullets.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md008_ul_style.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md009_trailing_spaces.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md010_no_hard_tabs.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md011_no_reversed_links.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md012_no_multiple_blanks.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md013_line_length.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md014_commands_show_output.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md015_no_missing_space_after_list_marker.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md018_no_missing_space_atx.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md019_no_multiple_space_atx.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md020_no_missing_space_closed_atx.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md021_no_multiple_space_closed_atx.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md022_blanks_around_headings.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md023_heading_start_left.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md024_no_duplicate_heading.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md025_single_title.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md026_no_trailing_punctuation.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md027_multiple_spaces_blockquote.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md028_no_blanks_blockquote.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md029_ordered_list_prefix.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md030_list_marker_space.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md031_blanks_around_fences.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md032_blanks_around_lists.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md033_no_inline_html.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md034_no_bare_urls.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md036_no_emphasis_only_first.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md037_spaces_around_emphasis.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md038_no_space_in_code.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md039_no_space_in_links.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md040_fenced_code_language.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md041_first_line_heading.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md042_no_empty_links.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md043_required_headings.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md044_proper_names.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md045_no_alt_text.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md046_code_block_style.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md047_single_trailing_newline.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md048_code_fence_style.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md049_emphasis_style.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md050_strong_style.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md051_link_fragments.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md052_reference_links_images.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md053_link_image_reference_definitions.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md054_link_image_style.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md055_table_pipe_style.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md056_table_column_count.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md057_existing_relative_links.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md058_blanks_around_tables.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/mod.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/strong_style.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/code_block_utils.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/document_structure.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/early_returns.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/markdown_elements.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/mod.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/range_utils.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/regex_cache.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/advanced_integration_tests.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/cli_duplication_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/cli_integration_tests.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/commonmark_compliance_tests.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/comprehensive_integration_tests.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/config_application_tests.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/config_tests.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/init_command_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/init_tests.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/integration_tests.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/lib.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/markdownlint_cli_integration.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/markdownlint_config_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/md030_edge_cases.md +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/output_format_tests.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/perf_check.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/pyproject_config_tests.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md001_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md001_unicode_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md002_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md003_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md004_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md005_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md006_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md006_unicode_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md008_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md009_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md010_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md011_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md012_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md013_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md014_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md015_proptest.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md015_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md018_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md019_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md020_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md021_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md022_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md023_extended_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md023_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md024_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md025_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md026_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md027_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md028_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md029_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md030_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md031_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md032_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md033_extended_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md033_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md034_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md036_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md037_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md038_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md039_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md040_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md041_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md042_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md043_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md044_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md045_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md046_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md047_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md048_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md049_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md050_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md051_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md052_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md053_additional_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md053_proptest.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md053_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md054_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md054_unicode_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md055_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md056_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md057_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md058_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/mod.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils/blockquote_utils_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils/code_block_utils_extended_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils/code_block_utils_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils/core_utils_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils/front_matter_utils_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils/line_index_test.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils/mod.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils_markdown_edge_cases.rs +0 -0
- {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils_tests.rs +0 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/// Rule MD007: Unordered list indentation
|
|
2
|
+
///
|
|
3
|
+
/// See [docs/md007.md](../../docs/md007.md) for full documentation, configuration, and examples.
|
|
4
|
+
use crate::rule::{Fix, LintError, LintResult, LintWarning, Rule, RuleCategory, Severity};
|
|
5
|
+
use crate::utils::document_structure::{DocumentStructure, DocumentStructureExtensions};
|
|
6
|
+
use crate::utils::element_cache::{ElementCache, ListMarkerType};
|
|
7
|
+
use crate::utils::range_utils::LineIndex;
|
|
8
|
+
use lazy_static::lazy_static;
|
|
9
|
+
use regex::Regex;
|
|
10
|
+
use toml;
|
|
11
|
+
|
|
12
|
+
#[derive(Debug, Clone)]
|
|
13
|
+
pub struct MD007ULIndent {
|
|
14
|
+
pub indent: usize,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
impl Default for MD007ULIndent {
|
|
18
|
+
fn default() -> Self {
|
|
19
|
+
Self { indent: 2 }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
impl MD007ULIndent {
|
|
24
|
+
pub fn new(indent: usize) -> Self {
|
|
25
|
+
Self { indent }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// Detect code blocks, including those inside blockquotes
|
|
29
|
+
fn compute_code_block_lines(content: &str) -> std::collections::HashSet<usize> {
|
|
30
|
+
let mut code_block_lines = std::collections::HashSet::new();
|
|
31
|
+
let mut in_code_block = false;
|
|
32
|
+
let mut fence: Option<String> = None;
|
|
33
|
+
for (i, line) in content.lines().enumerate() {
|
|
34
|
+
let trimmed = line.trim_start();
|
|
35
|
+
if !in_code_block {
|
|
36
|
+
if trimmed.starts_with("```") || trimmed.starts_with("~~~") {
|
|
37
|
+
in_code_block = true;
|
|
38
|
+
fence = Some(trimmed[..3].to_string());
|
|
39
|
+
code_block_lines.insert(i + 1);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
code_block_lines.insert(i + 1);
|
|
44
|
+
if let Some(ref f) = fence {
|
|
45
|
+
if trimmed.starts_with(f) {
|
|
46
|
+
in_code_block = false;
|
|
47
|
+
fence = None;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
code_block_lines
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#[allow(dead_code)]
|
|
57
|
+
fn is_in_code_block(content: &str, line_idx: usize) -> bool {
|
|
58
|
+
lazy_static! {
|
|
59
|
+
static ref CODE_BLOCK_MARKER: Regex = Regex::new(r"^(```|~~~)").unwrap();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let lines: Vec<&str> = content.lines().collect();
|
|
63
|
+
let mut in_code_block = false;
|
|
64
|
+
|
|
65
|
+
for (i, line) in lines.iter().enumerate() {
|
|
66
|
+
if i > line_idx {
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if CODE_BLOCK_MARKER.is_match(line.trim_start()) {
|
|
71
|
+
in_code_block = !in_code_block;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if i == line_idx {
|
|
75
|
+
return in_code_block;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
false
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
impl Rule for MD007ULIndent {
|
|
84
|
+
fn name(&self) -> &'static str {
|
|
85
|
+
"MD007"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fn description(&self) -> &'static str {
|
|
89
|
+
"Unordered list indentation"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
fn check(&self, ctx: &crate::lint_context::LintContext) -> LintResult {
|
|
93
|
+
let content = ctx.content;
|
|
94
|
+
let element_cache = ElementCache::new(content);
|
|
95
|
+
let mut warnings = Vec::new();
|
|
96
|
+
for item in element_cache.get_list_items() {
|
|
97
|
+
// Only unordered list items
|
|
98
|
+
// Skip list items inside code blocks (including YAML/front matter)
|
|
99
|
+
if element_cache.is_in_code_block(item.line_number) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if matches!(item.marker_type, ListMarkerType::Asterisk | ListMarkerType::Plus | ListMarkerType::Minus) {
|
|
103
|
+
let expected_indent = item.nesting_level * self.indent;
|
|
104
|
+
println!(
|
|
105
|
+
"MD007 DEBUG: line {} | indent={} | nesting={} | expected={} | emit_warning={}",
|
|
106
|
+
item.line_number,
|
|
107
|
+
item.indentation,
|
|
108
|
+
item.nesting_level,
|
|
109
|
+
expected_indent,
|
|
110
|
+
item.indentation != expected_indent
|
|
111
|
+
);
|
|
112
|
+
if item.indentation != expected_indent {
|
|
113
|
+
warnings.push(LintWarning {
|
|
114
|
+
rule_name: Some(self.name()),
|
|
115
|
+
message: format!(
|
|
116
|
+
"Incorrect indentation: expected {} spaces for nesting level {}, found {}",
|
|
117
|
+
expected_indent, item.nesting_level, item.indentation
|
|
118
|
+
),
|
|
119
|
+
line: item.line_number,
|
|
120
|
+
column: item.blockquote_prefix.len() + item.indentation + 1, // correct column for marker
|
|
121
|
+
severity: Severity::Warning,
|
|
122
|
+
fix: None,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
Ok(warnings)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// Optimized check using document structure
|
|
131
|
+
fn check_with_structure(
|
|
132
|
+
&self,
|
|
133
|
+
ctx: &crate::lint_context::LintContext,
|
|
134
|
+
_doc_structure: &DocumentStructure,
|
|
135
|
+
) -> LintResult {
|
|
136
|
+
self.check(ctx)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
fn fix(&self, ctx: &crate::lint_context::LintContext) -> Result<String, LintError> {
|
|
140
|
+
let content = ctx.content;
|
|
141
|
+
let element_cache = ElementCache::new(content);
|
|
142
|
+
let mut lines: Vec<&str> = content.lines().collect();
|
|
143
|
+
for item in element_cache.get_list_items() {
|
|
144
|
+
if element_cache.is_in_code_block(item.line_number) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if matches!(item.marker_type, ListMarkerType::Asterisk | ListMarkerType::Plus | ListMarkerType::Minus) {
|
|
148
|
+
let expected_indent = item.nesting_level * self.indent;
|
|
149
|
+
if item.indentation != expected_indent {
|
|
150
|
+
// Reconstruct the line: blockquote_prefix + correct_indent + marker + spaces_after_marker + content
|
|
151
|
+
let correct_indent = " ".repeat(expected_indent);
|
|
152
|
+
let marker = &item.marker;
|
|
153
|
+
let after_marker = " ".repeat(item.spaces_after_marker.max(1));
|
|
154
|
+
let new_line = format!(
|
|
155
|
+
"{}{}{}{}{}",
|
|
156
|
+
item.blockquote_prefix,
|
|
157
|
+
correct_indent,
|
|
158
|
+
marker,
|
|
159
|
+
after_marker,
|
|
160
|
+
item.content
|
|
161
|
+
);
|
|
162
|
+
lines[item.line_number - 1] = Box::leak(new_line.into_boxed_str());
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
Ok(lines.join("\n"))
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/// Get the category of this rule for selective processing
|
|
170
|
+
fn category(&self) -> RuleCategory {
|
|
171
|
+
RuleCategory::List
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/// Check if this rule should be skipped
|
|
175
|
+
fn should_skip(&self, ctx: &crate::lint_context::LintContext) -> bool {
|
|
176
|
+
ctx.content.is_empty()
|
|
177
|
+
|| (!ctx.content.contains('*')
|
|
178
|
+
&& !ctx.content.contains('-')
|
|
179
|
+
&& !ctx.content.contains('+'))
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
fn as_any(&self) -> &dyn std::any::Any {
|
|
183
|
+
self
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
fn default_config_section(&self) -> Option<(String, toml::Value)> {
|
|
187
|
+
let mut map = toml::map::Map::new();
|
|
188
|
+
map.insert(
|
|
189
|
+
"indent".to_string(),
|
|
190
|
+
toml::Value::Integer(self.indent as i64),
|
|
191
|
+
);
|
|
192
|
+
Some((self.name().to_string(), toml::Value::Table(map)))
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
fn from_config(config: &crate::config::Config) -> Box<dyn Rule>
|
|
196
|
+
where
|
|
197
|
+
Self: Sized,
|
|
198
|
+
{
|
|
199
|
+
let indent =
|
|
200
|
+
crate::config::get_rule_config_value::<usize>(config, "MD007", "indent").unwrap_or(2);
|
|
201
|
+
Box::new(MD007ULIndent::new(indent))
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
impl DocumentStructureExtensions for MD007ULIndent {
|
|
206
|
+
fn has_relevant_elements(
|
|
207
|
+
&self,
|
|
208
|
+
_ctx: &crate::lint_context::LintContext,
|
|
209
|
+
doc_structure: &DocumentStructure,
|
|
210
|
+
) -> bool {
|
|
211
|
+
// Use the document structure to check if there are any unordered list elements
|
|
212
|
+
!doc_structure.list_lines.is_empty()
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
#[cfg(test)]
|
|
217
|
+
mod tests {
|
|
218
|
+
use super::*;
|
|
219
|
+
use crate::lint_context::LintContext;
|
|
220
|
+
|
|
221
|
+
// Remove test_with_document_structure, as the rule no longer uses DocumentStructure for its logic
|
|
222
|
+
}
|
|
@@ -57,23 +57,35 @@ impl MD035HRStyle {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
let line = lines[i].trim();
|
|
60
|
-
|
|
61
60
|
let prev_line = lines[i - 1].trim();
|
|
62
61
|
|
|
63
|
-
// Check if the current line is all dashes or equals signs
|
|
64
|
-
|
|
65
62
|
let is_dash_line = !line.is_empty() && line.chars().all(|c| c == '-');
|
|
66
|
-
|
|
67
63
|
let is_equals_line = !line.is_empty() && line.chars().all(|c| c == '=');
|
|
68
|
-
|
|
69
|
-
// Check if the previous line is not empty and not a horizontal rule
|
|
70
|
-
|
|
71
64
|
let prev_line_has_content = !prev_line.is_empty() && !Self::is_horizontal_rule(prev_line);
|
|
72
|
-
|
|
73
|
-
// If the current line is all dashes or equals signs and the previous line has content,
|
|
74
|
-
// it's likely a Setext heading
|
|
75
65
|
(is_dash_line || is_equals_line) && prev_line_has_content
|
|
76
66
|
}
|
|
67
|
+
|
|
68
|
+
/// Find the most prevalent HR style in the document (excluding setext headings)
|
|
69
|
+
fn most_prevalent_hr_style(lines: &[&str]) -> Option<String> {
|
|
70
|
+
use std::collections::HashMap;
|
|
71
|
+
let mut counts: HashMap<&str, usize> = HashMap::new();
|
|
72
|
+
let mut order: Vec<&str> = Vec::new();
|
|
73
|
+
for (i, line) in lines.iter().enumerate() {
|
|
74
|
+
if Self::is_horizontal_rule(line) && !Self::is_potential_setext_heading(lines, i) {
|
|
75
|
+
let style = line.trim();
|
|
76
|
+
let counter = counts.entry(style).or_insert(0);
|
|
77
|
+
*counter += 1;
|
|
78
|
+
if *counter == 1 {
|
|
79
|
+
order.push(style);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Find the style with the highest count, breaking ties by first encountered
|
|
84
|
+
counts
|
|
85
|
+
.iter()
|
|
86
|
+
.max_by_key(|&(style, count)| (*count, -(order.iter().position(|&s| s == *style).unwrap_or(usize::MAX) as isize)))
|
|
87
|
+
.map(|(style, _)| style.to_string())
|
|
88
|
+
}
|
|
77
89
|
}
|
|
78
90
|
|
|
79
91
|
impl Rule for MD035HRStyle {
|
|
@@ -90,21 +102,11 @@ impl Rule for MD035HRStyle {
|
|
|
90
102
|
let _line_index = LineIndex::new(content.to_string());
|
|
91
103
|
|
|
92
104
|
let mut warnings = Vec::new();
|
|
93
|
-
|
|
94
105
|
let lines: Vec<&str> = content.lines().collect();
|
|
95
106
|
|
|
96
|
-
// Use the configured style or find the
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// Find the first HR in the document
|
|
100
|
-
let mut first_style = "---".to_string(); // Default if none found
|
|
101
|
-
for (i, line) in lines.iter().enumerate() {
|
|
102
|
-
if Self::is_horizontal_rule(line) && !Self::is_potential_setext_heading(&lines, i) {
|
|
103
|
-
first_style = line.trim().to_string();
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
first_style
|
|
107
|
+
// Use the configured style or find the most prevalent HR style
|
|
108
|
+
let expected_style = if self.style.is_empty() || self.style == "consistent" {
|
|
109
|
+
Self::most_prevalent_hr_style(&lines).unwrap_or_else(|| "---".to_string())
|
|
108
110
|
} else {
|
|
109
111
|
self.style.clone()
|
|
110
112
|
};
|
|
@@ -148,21 +150,11 @@ impl Rule for MD035HRStyle {
|
|
|
148
150
|
let _line_index = LineIndex::new(content.to_string());
|
|
149
151
|
|
|
150
152
|
let mut result = Vec::new();
|
|
151
|
-
|
|
152
153
|
let lines: Vec<&str> = content.lines().collect();
|
|
153
154
|
|
|
154
|
-
// Use the configured style or find the
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
// Find the first HR in the document
|
|
158
|
-
let mut first_style = "---".to_string(); // Default if none found
|
|
159
|
-
for (i, line) in lines.iter().enumerate() {
|
|
160
|
-
if Self::is_horizontal_rule(line) && !Self::is_potential_setext_heading(&lines, i) {
|
|
161
|
-
first_style = line.trim().to_string();
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
first_style
|
|
155
|
+
// Use the configured style or find the most prevalent HR style
|
|
156
|
+
let expected_style = if self.style.is_empty() || self.style == "consistent" {
|
|
157
|
+
Self::most_prevalent_hr_style(&lines).unwrap_or_else(|| "---".to_string())
|
|
166
158
|
} else {
|
|
167
159
|
self.style.clone()
|
|
168
160
|
};
|
|
@@ -174,9 +166,6 @@ impl Rule for MD035HRStyle {
|
|
|
174
166
|
continue;
|
|
175
167
|
}
|
|
176
168
|
|
|
177
|
-
let _trimmed = line.trim();
|
|
178
|
-
|
|
179
|
-
// Simplify the horizontal rule detection and replacement
|
|
180
169
|
if Self::is_horizontal_rule(line) {
|
|
181
170
|
// Here we have a proper horizontal rule - replace it with the expected style
|
|
182
171
|
result.push(expected_style.clone());
|
|
@@ -10,8 +10,8 @@ lazy_static! {
|
|
|
10
10
|
static ref INDENTED_CODE_BLOCK_REGEX: Regex = Regex::new(r"^(\s{4,})(.+)$").unwrap();
|
|
11
11
|
|
|
12
12
|
// List detection patterns
|
|
13
|
-
static ref UNORDERED_LIST_REGEX: FancyRegex = FancyRegex::new(r"^(?P<indent>[ \t]*)(?P<marker>[*+-])(?P<after>[ \t]
|
|
14
|
-
static ref ORDERED_LIST_REGEX: FancyRegex = FancyRegex::new(r"^(?P<indent>[ \t]*)(?P<marker>\d+\.)(?P<after>[ \t]
|
|
13
|
+
static ref UNORDERED_LIST_REGEX: FancyRegex = FancyRegex::new(r"^(?P<indent>[ \t]*)(?P<marker>[*+-])(?P<after>[ \t]*)(?P<content>.*)$").unwrap();
|
|
14
|
+
static ref ORDERED_LIST_REGEX: FancyRegex = FancyRegex::new(r"^(?P<indent>[ \t]*)(?P<marker>\d+\.)(?P<after>[ \t]*)(?P<content>.*)$").unwrap();
|
|
15
15
|
|
|
16
16
|
// Inline code span pattern
|
|
17
17
|
static ref CODE_SPAN_REGEX: Regex = Regex::new(r"`+").unwrap();
|
|
@@ -62,6 +62,8 @@ pub struct ListItem {
|
|
|
62
62
|
pub spaces_after_marker: usize,
|
|
63
63
|
pub nesting_level: usize,
|
|
64
64
|
pub parent_line_number: Option<usize>,
|
|
65
|
+
pub blockquote_depth: usize, // Number of leading blockquote markers
|
|
66
|
+
pub blockquote_prefix: String, // The actual prefix (e.g., "> > ")
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
/// Cache for Markdown document structural elements
|
|
@@ -279,31 +281,92 @@ impl ElementCache {
|
|
|
279
281
|
fn populate_list_items(&mut self) {
|
|
280
282
|
if let Some(content) = &self.content {
|
|
281
283
|
let lines: Vec<&str> = content.lines().collect();
|
|
282
|
-
let mut prev_items: Vec<(usize, usize)> = Vec::new(); // (nesting_level, line_number)
|
|
284
|
+
let mut prev_items: Vec<(usize, usize, usize)> = Vec::new(); // (blockquote_depth, nesting_level, line_number)
|
|
283
285
|
for (i, line) in lines.iter().enumerate() {
|
|
284
286
|
// Reset stack on blank lines
|
|
285
287
|
if line.trim().is_empty() {
|
|
286
288
|
prev_items.clear(); // Reset nesting after blank line
|
|
287
289
|
continue;
|
|
288
290
|
}
|
|
289
|
-
//
|
|
290
|
-
|
|
291
|
+
// Parse and strip blockquote prefix
|
|
292
|
+
let (blockquote_depth, blockquote_prefix, rest) = Self::parse_blockquote_prefix(line);
|
|
293
|
+
// Always call parse_list_item and always push if Some
|
|
294
|
+
if let Some(item) = self.parse_list_item(rest, i + 1, &mut prev_items, blockquote_depth, blockquote_prefix.clone()) {
|
|
291
295
|
self.list_items.push(item);
|
|
292
296
|
self.list_line_map[i] = true;
|
|
293
|
-
continue; // If it's a list item, do not treat as code block
|
|
294
297
|
}
|
|
295
|
-
// If not a list item, mark as code block if indented (4+ spaces)
|
|
296
|
-
// (This is for completeness, but code block map is handled elsewhere)
|
|
297
298
|
}
|
|
298
299
|
}
|
|
299
300
|
}
|
|
300
301
|
|
|
302
|
+
/// Parse and strip all leading blockquote markers, returning (depth, prefix, rest_of_line)
|
|
303
|
+
fn parse_blockquote_prefix(line: &str) -> (usize, String, &str) {
|
|
304
|
+
let mut rest = line;
|
|
305
|
+
let mut prefix = String::new();
|
|
306
|
+
let mut depth = 0;
|
|
307
|
+
loop {
|
|
308
|
+
let trimmed = rest.trim_start();
|
|
309
|
+
if trimmed.starts_with('>') {
|
|
310
|
+
// Find the '>' and a single optional space
|
|
311
|
+
let after = &trimmed[1..];
|
|
312
|
+
let mut chars = after.chars();
|
|
313
|
+
let mut space_count = 0;
|
|
314
|
+
if let Some(' ') = chars.next() {
|
|
315
|
+
space_count = 1;
|
|
316
|
+
}
|
|
317
|
+
let (spaces, after_marker) = after.split_at(space_count);
|
|
318
|
+
prefix.push('>');
|
|
319
|
+
prefix.push_str(spaces);
|
|
320
|
+
rest = after_marker;
|
|
321
|
+
depth += 1;
|
|
322
|
+
} else {
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
(depth, prefix, rest)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/// Calculate the nesting level for a list item, considering blockquote depth
|
|
330
|
+
fn calculate_nesting_level(
|
|
331
|
+
&self,
|
|
332
|
+
indent: usize,
|
|
333
|
+
blockquote_depth: usize,
|
|
334
|
+
prev_items: &mut Vec<(usize, usize, usize)>,
|
|
335
|
+
) -> usize {
|
|
336
|
+
// Only consider previous items with the same blockquote depth
|
|
337
|
+
let mut nesting_level = 0;
|
|
338
|
+
if let Some(&(last_bq, last_indent, last_level)) = prev_items.iter().rev().find(|(bq, _, _)| *bq == blockquote_depth) {
|
|
339
|
+
if indent >= last_indent + 2 {
|
|
340
|
+
nesting_level = last_level + 1;
|
|
341
|
+
} else {
|
|
342
|
+
// Walk back to find the most recent indent <= current indent with same blockquote depth
|
|
343
|
+
for &(prev_bq, prev_indent, prev_level) in prev_items.iter().rev() {
|
|
344
|
+
if prev_bq == blockquote_depth && prev_indent <= indent {
|
|
345
|
+
nesting_level = prev_level;
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
// Remove stack entries with indent >= current indent and same blockquote depth
|
|
352
|
+
while let Some(&(prev_bq, prev_indent, _)) = prev_items.last() {
|
|
353
|
+
if prev_bq != blockquote_depth || prev_indent < indent {
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
prev_items.pop();
|
|
357
|
+
}
|
|
358
|
+
prev_items.push((blockquote_depth, indent, nesting_level));
|
|
359
|
+
nesting_level
|
|
360
|
+
}
|
|
361
|
+
|
|
301
362
|
/// Parse a line as a list item and determine its nesting level
|
|
302
363
|
fn parse_list_item(
|
|
303
364
|
&self,
|
|
304
365
|
line: &str,
|
|
305
366
|
line_num: usize,
|
|
306
|
-
prev_items: &mut Vec<(usize, usize)>,
|
|
367
|
+
prev_items: &mut Vec<(usize, usize, usize)>,
|
|
368
|
+
blockquote_depth: usize,
|
|
369
|
+
blockquote_prefix: String,
|
|
307
370
|
) -> Option<ListItem> {
|
|
308
371
|
match UNORDERED_LIST_REGEX.captures(line) {
|
|
309
372
|
Ok(Some(captures)) => {
|
|
@@ -323,13 +386,13 @@ impl ElementCache {
|
|
|
323
386
|
"-" => ListMarkerType::Minus,
|
|
324
387
|
_ => unreachable!(),
|
|
325
388
|
};
|
|
326
|
-
let nesting_level = self.calculate_nesting_level(indentation, prev_items);
|
|
327
|
-
// Find parent: most recent previous item with lower nesting_level
|
|
389
|
+
let nesting_level = self.calculate_nesting_level(indentation, blockquote_depth, prev_items);
|
|
390
|
+
// Find parent: most recent previous item with lower nesting_level and same blockquote depth
|
|
328
391
|
let parent_line_number = prev_items
|
|
329
392
|
.iter()
|
|
330
393
|
.rev()
|
|
331
|
-
.find(|(
|
|
332
|
-
.map(|(_, line_num)| *line_num);
|
|
394
|
+
.find(|(bq, _, level)| *bq == blockquote_depth && *level < nesting_level)
|
|
395
|
+
.map(|(_, _, line_num)| *line_num);
|
|
333
396
|
return Some(ListItem {
|
|
334
397
|
line_number: line_num,
|
|
335
398
|
indentation,
|
|
@@ -340,6 +403,8 @@ impl ElementCache {
|
|
|
340
403
|
spaces_after_marker: spaces,
|
|
341
404
|
nesting_level,
|
|
342
405
|
parent_line_number,
|
|
406
|
+
blockquote_depth,
|
|
407
|
+
blockquote_prefix,
|
|
343
408
|
});
|
|
344
409
|
}
|
|
345
410
|
Ok(None) => {
|
|
@@ -361,13 +426,13 @@ impl ElementCache {
|
|
|
361
426
|
.map_or("", |m| m.as_str())
|
|
362
427
|
.trim_start()
|
|
363
428
|
.to_string();
|
|
364
|
-
let nesting_level = self.calculate_nesting_level(indentation, prev_items);
|
|
365
|
-
// Find parent: most recent previous item with lower nesting_level
|
|
429
|
+
let nesting_level = self.calculate_nesting_level(indentation, blockquote_depth, prev_items);
|
|
430
|
+
// Find parent: most recent previous item with lower nesting_level and same blockquote depth
|
|
366
431
|
let parent_line_number = prev_items
|
|
367
432
|
.iter()
|
|
368
433
|
.rev()
|
|
369
|
-
.find(|(
|
|
370
|
-
.map(|(_, line_num)| *line_num);
|
|
434
|
+
.find(|(bq, _, level)| *bq == blockquote_depth && *level < nesting_level)
|
|
435
|
+
.map(|(_, _, line_num)| *line_num);
|
|
371
436
|
return Some(ListItem {
|
|
372
437
|
line_number: line_num,
|
|
373
438
|
indentation,
|
|
@@ -378,6 +443,8 @@ impl ElementCache {
|
|
|
378
443
|
spaces_after_marker: spaces,
|
|
379
444
|
nesting_level,
|
|
380
445
|
parent_line_number,
|
|
446
|
+
blockquote_depth,
|
|
447
|
+
blockquote_prefix,
|
|
381
448
|
});
|
|
382
449
|
}
|
|
383
450
|
Ok(None) => {}
|
|
@@ -385,39 +452,6 @@ impl ElementCache {
|
|
|
385
452
|
}
|
|
386
453
|
None
|
|
387
454
|
}
|
|
388
|
-
|
|
389
|
-
/// Calculate the nesting level for a list item
|
|
390
|
-
fn calculate_nesting_level(
|
|
391
|
-
&self,
|
|
392
|
-
indent: usize,
|
|
393
|
-
prev_items: &mut Vec<(usize, usize)>,
|
|
394
|
-
) -> usize {
|
|
395
|
-
// CommonMark: a list item is nested if it is indented at least 2 spaces more than the previous list item
|
|
396
|
-
let mut nesting_level = 0;
|
|
397
|
-
if let Some(&(last_indent, last_level)) = prev_items.last() {
|
|
398
|
-
if indent >= last_indent + 2 {
|
|
399
|
-
// Nested under previous
|
|
400
|
-
nesting_level = last_level + 1;
|
|
401
|
-
} else {
|
|
402
|
-
// Walk back to find the most recent indent <= current indent
|
|
403
|
-
for &(prev_indent, prev_level) in prev_items.iter().rev() {
|
|
404
|
-
if prev_indent <= indent {
|
|
405
|
-
nesting_level = prev_level;
|
|
406
|
-
break;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
// Remove stack entries with indent >= current indent
|
|
412
|
-
while let Some(&(prev_indent, _)) = prev_items.last() {
|
|
413
|
-
if prev_indent < indent {
|
|
414
|
-
break;
|
|
415
|
-
}
|
|
416
|
-
prev_items.pop();
|
|
417
|
-
}
|
|
418
|
-
prev_items.push((indent, nesting_level));
|
|
419
|
-
nesting_level
|
|
420
|
-
}
|
|
421
455
|
}
|
|
422
456
|
|
|
423
457
|
// Thread-local cache for sharing across rules
|