From 75f9a329d3dc28082e7b05695394aa96e28700d4 Mon Sep 17 00:00:00 2001 From: Immanuel Haffner Date: Fri, 20 Mar 2026 14:59:25 +0100 Subject: [PATCH] fix(tostring): recurse into nested inline elements inside bold/italic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit md_str.bold(), md_str.italic(), and md_str.bold_italic() stripped their delimiter characters but returned the raw inner text without further processing. This meant nested inline constructs like links, images, or code spans inside emphasis were never resolved. For example, **[bold link](https://example.com)** produced the visual string "[bold link](https://example.com)" (width 32) instead of the correct "󰌷 bold link" (width 11), causing massive column width miscalculations in rendered tables. Pass the stripped inner text through md_str.tostring() so that nested inline elements (hyperlinks, images, code spans, etc.) are properly reduced to their visual representation. This mirrors the established pattern already used in the sibling module visual_text.lua, where md_delims_star() calls visual.markdown(inner). --- lua/markview/renderers/markdown/tostring.lua | 7 +++-- test/tostring_recursion.md | 32 ++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 test/tostring_recursion.md diff --git a/lua/markview/renderers/markdown/tostring.lua b/lua/markview/renderers/markdown/tostring.lua index b51a6fa..fa9dece 100644 --- a/lua/markview/renderers/markdown/tostring.lua +++ b/lua/markview/renderers/markdown/tostring.lua @@ -168,7 +168,7 @@ md_str.bold = function (match) removed = string.gsub(match, "^%_%_", ""):gsub("%_%_$", ""); end - return removed; + return md_str.tostring(md_str.buffer, removed, false); ---|fE end @@ -192,7 +192,8 @@ md_str.bold_italic = function (match) r = math.min(be and #be or 0, af and #af or 0); end - return vim.fn.strpart(match, r, vim.fn.strchars(match) - (r + r)); + local removed = vim.fn.strpart(match, r, vim.fn.strchars(match) - (r + r)); + return md_str.tostring(md_str.buffer, removed, false); ---|fE end @@ -339,7 +340,7 @@ md_str.italic = function (match) removed = string.gsub(match, "^%_", ""):gsub("%_$", ""); end - return removed; + return md_str.tostring(md_str.buffer, removed, false); ---|fE end diff --git a/test/tostring_recursion.md b/test/tostring_recursion.md new file mode 100644 index 0000000..69d521c --- /dev/null +++ b/test/tostring_recursion.md @@ -0,0 +1,32 @@ +; Bold/italic wrapping inline elements — tostring recursion + +### Bold + link + +| Kind | Short | Long | +|------|-------|------| +| Bold + link | **[bold link](https://example.com)** | **[bold link with long URL](https://spec.commonmark.org/0.31.2/#emphasis-and-strong-emphasis-combined-with-links-and-images)** | +| Italic + link | *[italic link](https://example.com)* | *[italic link with long URL](https://spec.commonmark.org/0.31.2/#emphasis-and-strong-emphasis-combined-with-links-and-images)* | +| Bold-italic + link | ***[both](https://example.com)*** | ***[both with long URL](https://spec.commonmark.org/0.31.2/#emphasis-and-strong-emphasis-combined-with-links-and-images)*** | + +### Bold + image + +| Kind | Short | Long | +|------|-------|------| +| Bold + image | **![icon](https://example.com/i.svg)** | **![a]( https://raw.githubusercontent.com/nvim-treesitter/playground/master/assets/screenshot.png)** | +| Italic + image | *![icon](https://example.com/i.svg)* | *![a](https://raw.githubusercontent.com/nvim-treesitter/playground/master/assets/screenshot.png)* | + +### Bold + code + +| Kind | Short | Long | +|------|-------|------| +| Bold + code | **`short`** | **`vim.api.nvim_buf_set_extmark(buffer, ns, row, col, opts)`** | +| Italic + code | *`short`* | *`vim.api.nvim_buf_set_extmark(buffer, ns, row, col, opts)`* | + +### Mixed nesting + +| Description | Content | +|-------------|---------| +| Bold wrapping link + code | **[link](https://example.com) and `code`** | +| Plain bold (no nesting) | **just bold text** | +| Plain italic | *just italic text* | +| No emphasis | [link](https://example.com) then `code` |