use assert_cmd::Command;
use mdtablefix::renumber_lists;
use rstest::rstest;
#[macro_use]
#[path = "common/mod.rs"]
mod common;
fn assert_is_a_renumbering_fixed_point(input: &[String], expected: &[String]) {
let once = renumber_lists(input);
assert_eq!(once, expected);
assert_eq!(renumber_lists(&once), once, "a second pass changes nothing");
}
fn assert_cli_leaves_alone(input: &'static str) {
Command::cargo_bin("mdtablefix")
.expect("Failed to create cargo command for mdtablefix")
.arg("--renumber")
.write_stdin(input)
.assert()
.success()
.stdout(input);
}
#[test]
fn restart_after_equal_indent_paragraph() {
let input = lines_vec!("1. One", "", "Paragraph", "3. Next");
let expected = lines_vec!("1. One", "", "Paragraph", "3. Next");
assert_eq!(renumber_lists(&input), expected);
}
#[test]
fn no_restart_without_blank() {
let input = lines_vec!("1. One", "Paragraph", "3. Next");
let expected = lines_vec!("1. One", "Paragraph", "2. Next");
assert_eq!(renumber_lists(&input), expected);
}
#[test]
fn an_indented_paragraph_left_of_the_content_column_ends_the_list() {
let input = lines_vec!("1. One", "", " Indented", "3. Next");
let expected = lines_vec!("1. One", "", " Indented", "3. Next");
assert_eq!(renumber_lists(&input), expected);
}
#[test]
fn restart_after_top_heading() {
let input = lines_vec!("1. One", "", "# Heading", "3. Next");
let expected = lines_vec!("1. One", "", "# Heading", "1. Next");
assert_eq!(renumber_lists(&input), expected);
}
#[test]
fn restart_after_nested_paragraph() {
let input = lines_vec!("1. One", " 1. Sub", "", "Paragraph", "3. Next");
let expected = lines_vec!("1. One", " 1. Sub", "", "Paragraph", "3. Next");
assert_eq!(renumber_lists(&input), expected);
}
#[test]
fn restart_after_nested_equal_indent_paragraph() {
let input = lines_vec!("1. One", " 1. Sub", "", " Paragraph", " 5. Next");
let expected = lines_vec!("1. One", " 1. Sub", "", " Paragraph", " 5. Next");
assert_eq!(renumber_lists(&input), expected);
}
#[test]
fn restart_after_formatting_paragraph() {
let input = lines_vec!("1. Start", "", "**Bold intro**", "", "4. Next");
let expected = lines_vec!("1. Start", "", "**Bold intro**", "", "1. Next");
assert_eq!(renumber_lists(&input), expected);
}
#[test]
fn reset_on_heading_and_thematic_break() {
let input = lines_vec!("1. a", "2. b", "# Heading", "1. c", "---", "5. d");
let expected = lines_vec!("1. a", "2. b", "# Heading", "1. c", "---", "1. d");
assert_eq!(renumber_lists(&input), expected);
}
#[rstest::rstest]
#[case::quoted_break("> ---")]
#[case::quoted_heading("> # Heading")]
fn a_quote_ends_the_list_it_interrupts(#[case] quoted_line: &str) {
let input = lines_vec!("1. first", "2. second", quoted_line, "8. third");
let expected = lines_vec!("1. first", "2. second", quoted_line, "1. third");
assert_eq!(renumber_lists(&input), expected);
}
#[test]
fn test_cli_renumber_option() {
Command::cargo_bin("mdtablefix")
.expect("Failed to create cargo command for mdtablefix")
.arg("--renumber")
.write_stdin("1. a\n4. b\n")
.assert()
.success()
.stdout("1. a\n2. b\n");
}
#[test]
fn nested_lists_respect_fence_tracker() {
let input = lines_vec![
"1. Outer list",
" ```",
" 1. Code block list",
" ```",
"9. Outer list continued",
" 4. Nested list",
" ```",
" - Malformed fence",
" ```",
" 8. Nested list continued",
];
let expected = lines_vec![
"1. Outer list",
" ```",
" 1. Code block list",
" ```",
"2. Outer list continued",
" 4. Nested list",
" ```",
" - Malformed fence",
" ```",
" 1. Nested list continued",
];
assert_eq!(renumber_lists(&input), expected);
}
#[test]
fn malformed_fences_do_not_break_list_renumbering() {
let input = lines_vec![
"1. List before fence",
" ```",
" 1. Inside code block",
" ``",
" still inside fence",
" ```",
"7. List after fence",
];
let expected = lines_vec![
"1. List before fence",
" ```",
" 1. Inside code block",
" ``",
" still inside fence",
" ```",
"2. List after fence",
];
assert_eq!(renumber_lists(&input), expected);
}
#[rstest(
input,
expected,
case::basic(
lines_vec!["1. first", "2. second", "7. third"],
lines_vec!["1. first", "2. second", "3. third"]
),
// A fence at the list's marker column ends the list (issue #563).
case::with_fence(
lines_vec!["1. item", "```", "code", "```", "9. next"],
lines_vec!["1. item", "```", "code", "```", "1. next"]
),
case::nested_lists(
lines_vec!["1. first", " 1. sub first", " 3. sub second", "2. second"],
lines_vec!["1. first", " 1. sub first", " 2. sub second", "2. second"]
),
case::tabs_in_indent(
lines_vec!["1. first", "\t1. sub first", "\t5. sub second", "2. second"],
lines_vec!["1. first", "\t1. sub first", "\t2. sub second", "2. second"]
),
case::mult_paragraph_items(
lines_vec!["1. first", "", " still first paragraph", "", "2. second"],
lines_vec!["1. first", "", " still first paragraph", "", "2. second"]
),
case::table_in_list(
lines_vec!["1. first", " | A | B |", " | 1 | 2 |", "5. second"],
lines_vec!["1. first", " | A | B |", " | 1 | 2 |", "2. second"]
),
case::restart_after_paragraph(
include_lines!("data/renumber_paragraph_restart_input.txt"),
include_lines!("data/renumber_paragraph_restart_expected.txt")
),
case::restart_after_formatting(
include_lines!("data/renumber_formatting_paragraph_input.txt"),
include_lines!("data/renumber_formatting_paragraph_expected.txt")
),
case::restart_after_break(
include_lines!("data/renumber_break_restart_input.txt"),
include_lines!("data/renumber_break_restart_expected.txt")
),
case::restart_after_heading(
include_lines!("data/renumber_heading_restart_input.txt"),
include_lines!("data/renumber_heading_restart_expected.txt")
),
case::restart_after_break_and_heading(
include_lines!("data/renumber_break_heading_restart_input.txt"),
include_lines!("data/renumber_break_heading_restart_expected.txt")
),
case::blank_lines(
include_lines!("data/renumber_blank_lines_input.txt"),
include_lines!("data/renumber_blank_lines_expected.txt")
),
case::ordered_list(
include_lines!("data/renumber_ordered_list_input.txt"),
include_lines!("data/renumber_ordered_list_expected.txt")
)
)]
fn test_renumber_cases(input: Vec<String>, expected: Vec<String>) {
assert_eq!(renumber_lists(&input), expected);
}
#[rstest]
#[case::nested_heading(include_lines!("data/issue_450_nested_heading_input.txt"))]
#[case::nested_break(include_lines!("data/issue_450_nested_break_input.txt"))]
fn renumber_issue_450_block_inside_an_item_keeps_the_list_counting(#[case] input: Vec<String>) {
assert_is_a_renumbering_fixed_point(&input, &input);
}
#[rstest]
#[case::heading_at_nested_marker_column(
lines_vec![
"1. Outer", " 1. Inner", " 2. Inner", "", " ## Heading in the outer item", "",
" 5. Inner again", "7. Outer again",
],
lines_vec![
"1. Outer", " 1. Inner", " 2. Inner", "", " ## Heading in the outer item", "",
" 1. Inner again", "2. Outer again",
]
)]
#[case::break_at_nested_marker_column(
lines_vec!["1. Outer", " 1. Inner", "", " ---", "", " 4. Inner again", "6. Outer again"],
lines_vec!["1. Outer", " 1. Inner", "", " ---", "", " 1. Inner again", "2. Outer again"]
)]
#[case::heading_three_spaces_inside_item(
lines_vec!["1. a", "", " ### h", "", "5. b"],
lines_vec!["1. a", "", " ### h", "", "2. b"]
)]
#[case::heading_two_spaces_ends_list(
lines_vec!["1. a", "", " ### h", "", "5. b"],
lines_vec!["1. a", "", " ### h", "", "1. b"]
)]
#[case::heading_one_space_ends_list(
lines_vec!["1. a", "", " ### h", "", "5. b"],
lines_vec!["1. a", "", " ### h", "", "1. b"]
)]
#[case::break_two_spaces_ends_list(
lines_vec!["1. a", "", " ***", "", "5. b"],
lines_vec!["1. a", "", " ***", "", "1. b"]
)]
#[case::heading_at_column_four_between_items(
lines_vec![
"1. Outer", " 1. Inner", "", " #### Heading", "", " 5. Inner again", "7. Outer again",
],
lines_vec![
"1. Outer", " 1. Inner", "", " #### Heading", "", " 1. Inner again", "2. Outer again",
]
)]
#[case::heading_left_of_a_tab_separated_item(
lines_vec!["1. \titem", "", " ### h", "", "5. b"],
lines_vec!["1. \titem", "", " ### h", "", "1. b"]
)]
#[case::heading_after_a_narrowed_marker(
lines_vec!["9. a", "10. b", "", " ### h", "", "4. c"],
lines_vec!["1. a", "2. b", "", " ### h", "", "3. c"]
)]
#[case::heading_at_margin(
lines_vec!["1. a", "4. b", "", "# Title", "", "6. c", "9. d"],
lines_vec!["1. a", "2. b", "", "# Title", "", "1. c", "2. d"]
)]
#[case::break_at_margin(
lines_vec!["1. a", " 1. sub", "", "---", "", "6. c"],
lines_vec!["1. a", " 1. sub", "", "---", "", "1. c"]
)]
fn renumber_issue_450_neighbouring_shapes(
#[case] input: Vec<String>,
#[case] expected: Vec<String>,
) {
assert_is_a_renumbering_fixed_point(&input, &expected);
}
#[test]
fn renumber_issue_450_cli_keeps_counting_past_a_nested_heading() {
assert_cli_leaves_alone(include_str!("data/issue_450_nested_heading_input.txt"));
}
#[rstest]
#[case::fence(include_lines!("data/issue_563_fence_input.txt"))]
#[case::bullet_list(include_lines!("data/issue_563_bullet_list_input.txt"))]
#[case::table(include_lines!("data/issue_563_table_input.txt"))]
#[case::block_quote(include_lines!("data/issue_563_block_quote_input.txt"))]
#[case::html_comment(include_lines!("data/issue_563_html_comment_input.txt"))]
#[case::link_paragraph(include_lines!("data/issue_563_link_paragraph_input.txt"))]
fn renumber_issue_563_block_at_marker_column_ends_the_list(#[case] input: Vec<String>) {
assert_is_a_renumbering_fixed_point(&input, &input);
}
#[rstest]
#[case::fence_inside_item(
lines_vec!["1. a", " ```", " code", " ```", "5. b"],
lines_vec!["1. a", " ```", " code", " ```", "2. b"]
)]
#[case::bullet_inside_item(
lines_vec!["1. a", "", " - sub", "", "5. b"],
lines_vec!["1. a", "", " - sub", "", "2. b"]
)]
#[case::fence_at_nested_marker_column(
lines_vec![
"1. Outer", " 1. Inner", " 2. Inner", "", " ```", " code", " ```", "",
" 4. Inner again", "5. Outer again",
],
lines_vec![
"1. Outer", " 1. Inner", " 2. Inner", "", " ```", " code", " ```", "",
" 1. Inner again", "2. Outer again",
]
)]
#[case::bullet_interrupts_without_blank(
lines_vec!["1. a", "2. b", "- bullet", "", "3. c"],
lines_vec!["1. a", "2. b", "- bullet", "", "1. c"]
)]
#[case::quote_interrupts_without_blank(
lines_vec!["1. a", "> quote", "", "5. b"],
lines_vec!["1. a", "> quote", "", "1. b"]
)]
#[case::html_block_interrupts_without_blank(
lines_vec!["1. a", "<div>", "", "5. b"],
lines_vec!["1. a", "<div>", "", "1. b"]
)]
#[case::inline_tag_is_lazy_text(
lines_vec!["1. a", "<span>x", "4. b"],
lines_vec!["1. a", "<span>x", "2. b"]
)]
#[case::bare_bullet_is_lazy_text(lines_vec!["1. a", "-", "4. b"], lines_vec!["1. a", "-", "2. b"])]
#[case::non_breaking_space_is_no_bullet(
lines_vec!["1. a", "-\u{a0}text", "7. next"],
lines_vec!["1. a", "-\u{a0}text", "2. next"]
)]
#[case::paragraph_left_of_the_content_column(
lines_vec!["1. a", "", " para", "", "5. b"],
lines_vec!["1. a", "", " para", "", "1. b"]
)]
#[case::lazy_paragraph_continues(
lines_vec continuation", "3. b"],
lines_vec continuation", "2. b"]
)]
fn renumber_issue_563_neighbouring_shapes(
#[case] input: Vec<String>,
#[case] expected: Vec<String>,
) {
assert_is_a_renumbering_fixed_point(&input, &expected);
}
#[test]
fn renumber_issue_563_cli_keeps_the_restart_after_a_fence() {
assert_cli_leaves_alone(include_str!("data/issue_563_fence_input.txt"));
}