From 28596fc6594847058d7707132e1558cff87e4ac8 Mon Sep 17 00:00:00 2001 From: delphinus Date: Tue, 7 Jul 2026 00:17:11 +0900 Subject: [PATCH] fix(blockquote): render fenced code blocks inside plain blockquotes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit コードフェンス (```) を含む引用ブロックが正しく描画されず、``` が インラインの として扱われる問題を修正した。 原因は content_builder の引用内コードフェンス処理が current_alert_type (= コールアウト `> [!NOTE]` のとき)でのみ有効になっていたため。通常の `> ` 引用では current_alert_type が nil でこの分岐に入らず、`> ```sh` が インライン描画に流れて ``` が普通のコードスパンになっていた。 - ゲート条件を `current_alert_type and line:match "^>"` から `line:match "^>"` に緩め、通常の引用でもフェンスを認識させる - 分岐内の apply_alert_styling 2 か所を current_alert_type でガードし、 通常の引用にコールアウト用スタイルが掛からないようにする これで引用内コードブロックはフェンス行が本文化されず、│ プレフィックス 付きで String 描画され、code_blocks に登録されてシンタックスハイライトの 対象になる。回帰テストを 3 件追加。 Co-Authored-By: Claude Opus 4.8 (1M context) --- lua/md-render/content_builder.lua | 10 +++--- tests/markdown_checkbox_test.lua | 60 +++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/lua/md-render/content_builder.lua b/lua/md-render/content_builder.lua index e163aa3..538dae2 100644 --- a/lua/md-render/content_builder.lua +++ b/lua/md-render/content_builder.lua @@ -2147,8 +2147,8 @@ function ContentBuilder:render_document(lines, opts) else local handled = false - -- Handle code blocks inside callouts - if current_alert_type and line:match "^>" then + -- Handle code blocks inside blockquotes (both plain blockquotes and callouts) + if line:match "^>" then local stripped = line:gsub("^>%s?", "") if stripped:match "^```" then if not in_callout_code_block then @@ -2175,7 +2175,9 @@ function ContentBuilder:render_document(lines, opts) cb_hls[2].col = cb_icon_start end self:add_line(fname_line, cb_hls) - self:apply_alert_styling(lines_before, #self.lines, current_alert_type, false) + if current_alert_type then + self:apply_alert_styling(lines_before, #self.lines, current_alert_type, false) + end lines_shown = lines_shown + 1 end end @@ -2237,7 +2239,7 @@ function ContentBuilder:render_document(lines, opts) }) detect_urls_in_code_line(self, stripped, #callout_code_prefix, #code_line) end - self:apply_alert_styling(lines_before, #self.lines, current_alert_type, false) + if current_alert_type then self:apply_alert_styling(lines_before, #self.lines, current_alert_type, false) end handled = true end end diff --git a/tests/markdown_checkbox_test.lua b/tests/markdown_checkbox_test.lua index 1587563..48ee2df 100644 --- a/tests/markdown_checkbox_test.lua +++ b/tests/markdown_checkbox_test.lua @@ -1001,6 +1001,66 @@ test("multiple code spans with link: underline position correct", function() end end) +-- Fenced code blocks inside plain blockquotes +local function render_doc_full_cb(input_lines, opts) + local builder = ContentBuilder.new() + builder:render_document(input_lines, opts or { max_width = 80, indent = "" }) + return builder.lines, builder.highlights, builder.code_blocks +end + +test("blockquote code fence: fence lines are not rendered as text", function() + local lines = render_doc_full_cb { + "> hoge", + "> ```sh", + "> echo $FOO", + "> ```", + "> fuga", + } + assert_eq(#lines, 3, "bq code fence: only hoge/code/fuga lines rendered") + assert_eq(lines[1], "│ hoge", "bq code fence: first quote line") + assert_eq(lines[2], "│ echo $FOO", "bq code fence: code line keeps │ prefix, no backticks") + assert_eq(lines[3], "│ fuga", "bq code fence: last quote line") + for _, l in ipairs(lines) do + assert_eq(l:find "```" == nil, true, "bq code fence: no literal backtick fence in output") + end +end) + +test("blockquote code fence: registers a code block for syntax highlighting", function() + local _, _, code_blocks = render_doc_full_cb { + "> ```sh", + "> echo $FOO", + "> ```", + } + assert_eq(#code_blocks, 1, "bq code fence: one code block registered") + assert_eq(code_blocks[1].language, "sh", "bq code fence: language detected") + assert_eq(code_blocks[1].source_lines[1], "echo $FOO", "bq code fence: source line captured") +end) + +test("blockquote code fence: code content gets String highlight, prefix FloatBorder", function() + local _, highlights = render_doc_full_cb { + "> ```sh", + "> echo $FOO", + "> ```", + } + -- Only the code line is rendered (line index 0), fences consumed + local groups + for _, h in ipairs(highlights) do + if h.line == 0 then groups = h.groups end + end + assert_eq(groups ~= nil, true, "bq code fence: highlight groups present for code line") + if groups then + local has_border, has_string = false, false + for _, g in ipairs(groups) do + if g.hl == "FloatBorder" then has_border = true end + if g.hl == "String" then has_string = true end + -- must not be styled as an alert/callout + assert_eq(g.hl:find "MdRenderAlert" == nil, true, "bq code fence: no alert styling") + end + assert_eq(has_border, true, "bq code fence: │ prefix is FloatBorder") + assert_eq(has_string, true, "bq code fence: code content is String") + end +end) + -- Print summary print(string.format("\n%d passed, %d failed", pass_count, fail_count)) if fail_count > 0 then os.exit(1) end