diff --git a/crates/jp_md/src/format_tests.rs b/crates/jp_md/src/format_tests.rs index 3b434ebb4..30f8c1ef4 100644 --- a/crates/jp_md/src/format_tests.rs +++ b/crates/jp_md/src/format_tests.rs @@ -1342,6 +1342,22 @@ fn test_thematic_break_line_style_ignores_terminal_width() { ); } +#[test] +fn test_thematic_break_line_style_fits_inside_a_blockquote() { + // A blockquote's `> ` narrows the text column by two. A rule that ignored + // the prefix would overflow the line and wrap. + let mut formatter = Formatter::with_width(40); + formatter.hr_style = HrStyle::Line; + + let actual = formatter + .format_terminal("> above\n>\n> ---\n>\n> below") + .unwrap(); + assert!( + actual.contains(&"─".repeat(38)) && !actual.contains(&"─".repeat(39)), + "Expected a 38-char unicode line inside the quote.\nActual: {actual:?}" + ); +} + #[test] fn test_table_is_fitted_to_the_terminal_width() { // A table is laid out, not wrapped, so it has to fit the terminal on its diff --git a/crates/jp_md/src/render.rs b/crates/jp_md/src/render.rs index 8459d097f..b34bf483d 100644 --- a/crates/jp_md/src/render.rs +++ b/crates/jp_md/src/render.rs @@ -503,8 +503,11 @@ impl<'a, 'w> TerminalFormatter<'a, 'w> { } HrStyle::Line => { // A rule spans the text column, not the terminal, so it lines up - // with the wrapped prose above and below it. - let line: String = "─".repeat(self.writer.width.max(1)); + // with the wrapped prose above and below it. Inside a + // blockquote or a list item that column is narrowed by the + // prefix each line carries. + let width = self.writer.width.saturating_sub(self.writer.prefix_width()); + let line: String = "─".repeat(width.max(1)); self.writer.output(&line, false)?; } }