Skip to content

fix(x-markdown): render placeholder for inline construct after list marker - #1995

Open
zuokun300 wants to merge 2 commits into
ant-design:mainfrom
zuokun300:fix/streaming-list-inline-1988
Open

fix(x-markdown): render placeholder for inline construct after list marker#1995
zuokun300 wants to merge 2 commits into
ant-design:mainfrom
zuokun300:fix/streaming-list-inline-1988

Conversation

@zuokun300

@zuokun300 zuokun300 commented Jul 27, 2026

Copy link
Copy Markdown

What & Why

Fixes #1988.

While streaming, an inline construct that starts immediately after a list marker was rendered as raw markdown instead of an incomplete-token placeholder:

input (mid-stream) before after
- [text](htt - [text](htt (raw) - <incomplete-link … />
- ![alt](htt - ! + wrong link placeholder - <incomplete-image … />

Root cause

In useStreaming.ts, once - is seen the list recognizer owns the token and keeps appending to pending. Its getCommitPrefix only handed the buffer over to the inline-code recognizer (backtick). For a link/image the buffer stayed a "list" token, failed isStreamingValid, and was committed as plain text — so the placeholder was never produced.

Fix

Extend the list recognizer only:

  • isStreamingValid keeps the list token pending through the ambiguous window right after the marker, then releases it once the construct is confirmed (]( for a link, ![ for an image).
  • getCommitPrefix commits just the marker prefix (- ) and hands the rest to the link/image recognizer.

GFM task markers (- [ ] / - [x] / - [X]) never contain ](, so they keep rendering as list text — verified explicitly in the tests. This is the case a naive "hand over on any [" would break.

Tradeoff (worth a maintainer's eye)

For a link inside a list there is now a brief "blank" window from - [ until ]( arrives (the label is held, not shown as raw). This mirrors how the library already holds an incomplete emphasis in a list (- **bold renders nothing until closed), so it is consistent with existing behavior — but flagging it in case a different UX is preferred.

Scope

  • Ordered lists (1. [text]() were not affected (they don't go through the list recognizer) and continue to work.
  • Other markers (*, +) and indented list items are covered.

Testing

New streaming-list-inline.test.tsx (9 cases). Verified red→green:

  • On unmodified source: the link & image cases fail (raw output), the other 7 pass.
  • With this fix: 9/9 pass, and the existing hooks.test.tsx suite (96 cases) stays green — no regression.

Summary by CodeRabbit

  • Bug 修复
    • 优化流式解析:列表标记后紧跟行内链接、图片或行内代码时,不再错误回退显示为原始 Markdown。
    • 改进链接/图片占位符触发时机,避免出现孤立的 ! 等异常输出。
    • 提升 GFM 任务列表([ ]/[x]/[X] 及其变体)识别准确性,避免与链接占位符混淆;流式中途的裸任务标记会暂挂,结束后再正确渲染。
  • 测试
    • 新增针对相关回归场景的流式渲染用例覆盖。

…arker

When streaming, an inline construct (link/image) that starts right after a
list marker (`- [text](`, `- ![alt](`) was committed as raw markdown, because
the list recognizer only handed over to inline code (backticks).

Extend the list recognizer to keep the token pending through the ambiguous
window and hand over to the link/image recognizer once the construct is
confirmed (`](` for links, `![` for images). GFM task markers ([ ]/[x]/[X])
never contain `](`, so they keep rendering as list text.

Fixes ant-design#1988

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dosubot dosubot Bot added bug Something isn't working javascript Pull requests that update Javascript code labels Jul 27, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8cd5c3a4-db49-49d7-b20a-d2d1ee2af49a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

更新流式 Markdown 解析中列表 token 的交接与前缀提交逻辑,使列表标记后的链接、图片和行内代码可正确识别,并新增相关回归测试及 GFM 任务列表测试。

Changes

列表后的行内结构流式解析

Layer / File(s) Summary
列表 token 交接逻辑
packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts
根据后续的 !![[、任务列表标记及 ]( 链接结构,调整列表 token 的 pending、交接和前缀提交行为。
行内结构回归测试
packages/x-markdown/src/XMarkdown/__tests__/streaming-list-inline.test.tsx
新增列表后链接、图片、行内代码和单字符标签链接的占位符断言,验证 GFM 任务列表及非列表链接场景。

Estimated code review effort: 3 (Moderate) | ~20 minutes

Poem

小兔敲开流式门,
列表之后链接分。
图片代码不迷路,
任务方框守本真。
占位符稳稳接住,
Markdown 轻快前行。

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题清楚概括了列表标记后行内占位符渲染的主要修复。
Linked Issues check ✅ Passed 实现与 #1988 的目标一致,已修复列表标记后不完整链接/图片的流式占位符渲染。
Out of Scope Changes check ✅ Passed 未发现明显超出 #1988 目标的改动,新增逻辑和测试都围绕流式列表内不完整行内结构展开。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/x-markdown/src/XMarkdown/__tests__/streaming-list-inline.test.tsx (1)

35-58: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

补齐 *+ 列表标记的回归覆盖。

当前所有用例都使用 -,但本次逻辑明确覆盖 -*+ 三种标记。建议将链接、图片和行内代码用例参数化到三种前缀,避免某一种列表标记的交接逻辑回归。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/x-markdown/src/XMarkdown/__tests__/streaming-list-inline.test.tsx`
around lines 35 - 58, Extend the inline construct tests in the “#1988 inline
construct right after a list marker” suite to cover -, *, and + list markers.
Parameterize the link, image, and inline-code cases so each prefix verifies the
same placeholder output while preserving the existing raw inline content and
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts`:
- Around line 132-134: Update the listPrefix matching in getCommitPrefix to
require 1–3 whitespace characters after -, +, or *, matching the
isStreamingValid rule; preserve the existing prefix extraction and handoff
behavior for valid GFM list markers.
- Around line 123-125: Update the task-marker checks in both isStreamingValid
and getCommitPrefix to require a whitespace character or end of input after the
closing bracket, using the shared boundary behavior ^\[[ xX]\](?:\s|$). Add
regression coverage for markers followed immediately by link text such as
“(htt”, while preserving valid task-marker handling.

---

Nitpick comments:
In `@packages/x-markdown/src/XMarkdown/__tests__/streaming-list-inline.test.tsx`:
- Around line 35-58: Extend the inline construct tests in the “#1988 inline
construct right after a list marker” suite to cover -, *, and + list markers.
Parameterize the link, image, and inline-code cases so each prefix verifies the
same placeholder output while preserving the existing raw inline content and
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 76b89158-8862-4167-9254-63bf73c392cc

📥 Commits

Reviewing files that changed from the base of the PR and between 13afbf4 and 5a09e25.

📒 Files selected for processing (2)
  • packages/x-markdown/src/XMarkdown/__tests__/streaming-list-inline.test.tsx
  • packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts

Comment thread packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts Outdated
Comment thread packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts Outdated
Address CodeRabbit review on ant-design#1988: "[x](url)" is a link whose label is
"x", not a GFM task marker. Decide by what follows "]" (whitespace = task,
"](" = link) instead of end-of-buffer, so a marker is not settled before
its "(" arrives during char-by-char streaming. Align getCommitPrefix's
list-prefix rule with isStreamingValid (require trailing whitespace).

Add regression tests for [x] / [X] / [ ] single-char labels.
@zuokun300

Copy link
Copy Markdown
Author

@coderabbitai review

Both findings addressed in 672011d. Note the reply on the first thread: the suggested (?:\s|$) boundary doesn't hold under char-by-char streaming ($ matches the buffer edge and settles [x] before its ( arrives), so the fix decides by what follows ] (\s = task, ]( = link). Local suite: 110/110 green, with red→green proof on the new regression tests.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== PR head and referenced commit =='
git status --short
git show --no-ext-diff --format=fuller --stat 672011d
git show --no-ext-diff --format= --find-renames 672011d -- \
  packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts \
  packages/x-markdown/src/XMarkdown/__tests__/streaming-list-inline.test.tsx

printf '%s\n' '== Implementation outline =='
ast-grep outline packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts --items all

printf '%s\n' '== Relevant recognizer and test excerpts =='
rg -n -C 12 'tokenRecognizerMap|List:|isStreamingValid|getCommitPrefix|task|incomplete-link|incomplete-image' \
  packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts \
  packages/x-markdown/src/XMarkdown/__tests__/streaming-list-inline.test.tsx

printf '%s\n' '== Related token definitions and recognition control flow =='
rg -n -C 8 'STREAM_INCOMPLETE_REGEX|recognize\s*\(|getCommitPrefix|isStreamingValid' \
  packages/x-markdown/src/XMarkdown

Length of output: 51795


@zuokun300 Confirmed. The revised rule correctly avoids treating the current buffer edge as a task-marker boundary: [x] remains pending until its next character distinguishes task text (\s) from a link (](). The matching getCommitPrefix condition and added [x], [X], and [ ] link-label regressions address the prior findings. No new concerns from this review.

🐇 ✅


✏️ Learnings added
Learnt from: zuokun300
URL: https://github.com/ant-design/x/pull/1995

Timestamp: 2026-07-27T22:59:44.944Z
Learning: In `packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts`, streaming input is processed character-by-character, so a regex end-of-buffer boundary (`$`) is not evidence that a Markdown construct is complete. For GFM-looking single-character labels following an unordered-list marker, distinguish a settled task marker by whitespace after `]` and a link by `](`; keep the token pending while it remains ambiguous.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working javascript Pull requests that update Javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

XMarkdown: incomplete link directly after a list marker renders as raw markdown during streaming

1 participant