From d31110cd67dad4ec907735912715659acc987b23 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 13 Aug 2026 16:18:50 -0600 Subject: [PATCH] fix: allow nested italics inside bold spans in markdown renderer --- src/render/markdown.rs | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/render/markdown.rs b/src/render/markdown.rs index be54fa0..8855c1d 100644 --- a/src/render/markdown.rs +++ b/src/render/markdown.rs @@ -39,8 +39,10 @@ static INLINE_CODE_RE: LazyLock = LazyLock::new(|| Regex::new(r"`([^`\n]+ static IMAGE_RE: LazyLock = LazyLock::new(|| Regex::new(r"!\[([^\]]*)\]\(([^)]+)\)").unwrap()); static LINK_RE: LazyLock = LazyLock::new(|| Regex::new(r"\[([^\]]+)\]\(([^)]+)\)").unwrap()); -static BOLD_AST_RE: LazyLock = LazyLock::new(|| Regex::new(r"\*\*([^*\n]+)\*\*").unwrap()); -static BOLD_US_RE: LazyLock = LazyLock::new(|| Regex::new(r"__([^_\n]+)__").unwrap()); +static BOLD_AST_RE: LazyLock = + LazyLock::new(|| Regex::new(r"\*\*((?:[^*\n]|\*(?!\*))+?)\*\*").unwrap()); +static BOLD_US_RE: LazyLock = + LazyLock::new(|| Regex::new(r"__((?:[^_\n]|_(?!_))+?)__").unwrap()); static ITALIC_AST_RE: LazyLock = LazyLock::new(|| Regex::new(r"(? = @@ -2419,6 +2421,36 @@ std::error::Error>> { assert!(result.contains("\x1b[9m"), "strikethrough SGR: {result:?}"); } + #[test] + fn bold_asterisk_wraps_italic_asterisk() { + let styles = test_styles(); + + let result = apply_inline("**loud *soft* loud**", &styles); + + assert!( + !result.contains("**"), + "outer bold markers stripped: {result:?}" + ); + assert!(result.contains("\x1b[1m"), "bold SGR present: {result:?}"); + assert!(result.contains("\x1b[3m"), "italic SGR present: {result:?}"); + assert!(result.contains("soft")); + } + + #[test] + fn bold_underscore_wraps_italic_underscore() { + let styles = test_styles(); + + let result = apply_inline("__loud _soft_ loud__", &styles); + + assert!( + !result.contains("__"), + "outer bold markers stripped: {result:?}" + ); + assert!(result.contains("\x1b[1m"), "bold SGR present: {result:?}"); + assert!(result.contains("\x1b[3m"), "italic SGR present: {result:?}"); + assert!(result.contains("soft")); + } + #[test] fn bold_wraps_inline_code() { let styles = test_styles();