Skip to content

feat: rewrite markdown preview rendering pipeline - #4693

Merged
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
Johnson-zs:feat/markdown-preview-richtext
Oct 28, 2026
Merged

deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
Johnson-zs:feat/markdown-preview-richtext

Conversation

@Johnson-zs

@Johnson-zs Johnson-zs commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor
  1. The markdown preview only called QTextDocument::setMarkdown(), which
    parses CommonMark but produces no visual formatting at all: no heading
    scale, no spacing, no code surface, no table borders. Rendering quality
    was far below expectations
  2. Parse with md4c (the parser Qt uses internally, so the dialect still
    matches) and emit the HTML subset QTextHtmlParser actually honours -
    the same round-trip format QTextDocument::toHtml() produces. Inline
    style attributes carry the theme
  3. md4c exposes the fence info string that Qt's reader discards, so code
    blocks are re-coloured per block with KSyntaxHighlighting
    (AbstractHighlighter::highlightLine + QTextCursor::mergeCharFormat),
    giving each block its own language
  4. All colours derive from DGuiApplicationHelper/DPalette and the document
    is rebuilt on themeTypeChanged, so light/dark switch keeps the reading
    position
  5. Work around four QtHTML limits found by rendering to PNG: h1-h6 ignore
    font-size (headings are emitted as sized bold paragraphs), block
    margins only honour px (pt is dropped), blockquote cannot draw a left
    bar and hr cannot take a colour (both use a one cell table with a
    border), and nested lists need margin-left on every level
  6. Add libmd4c-dev to Build-Depends in both debian/control and
    debian/control.in; it is pure C with Architecture: any and present for
    amd64/arm64/loong64/riscv64

Log: Rewrite markdown preview with md4c parsing, themed rich text layout
and per-block code syntax highlighting

Influence:

  1. Open any .md file in the file manager preview panel and confirm
    headings, paragraphs, lists, quotes, tables, images and fenced code
    blocks are properly typeset
  2. Confirm fenced code blocks are syntax highlighted according to their
    info string (cpp, python, ```json and so on)
  3. Switch the system theme between light and dark and confirm the preview
    recolours without jumping back to the top
  4. Open a markdown file with relative image references and confirm the
    images resolve against the file directory
  5. Run ut-markdown-preview and confirm all cases pass

feat: 重写 markdown 预览渲染管线

  1. 原实现只调用了一句 QTextDocument::setMarkdown(),该接口只解析
    CommonMark 语法,完全不产生任何排版样式:没有标题字号阶梯、没有
    段间距、没有代码块底色、没有表格边框,观感很差
  2. 改用 md4c 解析(Qt 内部 setMarkdown 用的就是它,方言保持一致),
    只生成 QtHtmlParser 真正支持的 HTML 子集——即
    QTextDocument::toHtml() 自身的往返格式,配色通过内联样式属性携带
  3. md4c 能给出 Qt 读取器丢弃的 fence 语言信息,因此导入后按块用
    KSyntaxHighlighting 二次着色
    (AbstractHighlighter::highlightLine + QTextCursor::mergeCharFormat),
    每个代码块都能使用自己的语言定义
  4. 配色全部从 DGuiApplicationHelper/DPalette 派生,themeTypeChanged
    时整体重渲染,明暗切换保留阅读位置
  5. 通过离屏渲染 PNG 实测,绕开四处 QtHTML 限制:h1-h6 忽略字号
    (标题改为带字号的粗体段落)、块边距只认 px(pt 被丢弃)、
    blockquote 无法画左侧色条且 hr 无法设色(两者改用单格表格加边框)、
    嵌套列表每一层都要写 margin-left
  6. 在 debian/control 与 debian/control.in 的 Build-Depends 中新增
    libmd4c-dev;该包为纯 C、Architecture: any,
    amd64/arm64/loong64/riscv64 均已提供

Log: 重写 markdown 预览,改用 md4c 解析、跟随主题的富文本排版,并支持
按代码块语言语法高亮

Influence:

  1. 在文件管理器预览面板打开任意 .md 文件,确认标题、段落、列表、
    引用、表格、图片与代码块排版正常
  2. 确认 fenced 代码块按其 info string 语法高亮(cpp、python、
  3. 切换系统明暗主题,确认预览重新着色且不会跳回顶部
  4. 打开含相对路径图片引用的 markdown 文件,确认图片按文件所在目录
    正确解析
  5. 运行 ut-markdown-preview,确认全部用例通过

Summary by Sourcery

Rewrite the Markdown preview pipeline to provide themed rich-text rendering with per-block syntax highlighting and reliable resource handling.

New Features:

  • Render Markdown previews with themed rich-text formatting for headings, paragraphs, lists, quotes, tables, images, links, and code blocks.
  • Syntax-highlight fenced code blocks according to their declared languages.
  • Resolve relative local images and scale them to fit the preview area.
  • Rebuild previews when the system theme changes while preserving the reader's position.

Bug Fixes:

  • Replace the unformatted Markdown preview output with visually styled and readable rendering.
  • Handle Qt rich-text rendering limitations so themed heading sizes, spacing, borders, and nested list indentation are preserved.

Enhancements:

  • Centralize Markdown palette, typography, layout metrics, HTML generation, document rendering, and code highlighting into dedicated components.

Build:

  • Add md4c and KDE Syntax Highlighting dependencies to the preview and test builds.
  • Declare libmd4c-dev as a Debian build dependency.

Tests:

  • Add comprehensive unit coverage for themed rendering, Markdown-to-HTML conversion, syntax highlighting, image handling, theme changes, and scroll-position behavior.

@deepin-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Johnson-zs

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai

sourcery-ai Bot commented Oct 27, 2026

Copy link
Copy Markdown

Reviewer's Guide

The preview now parses Markdown with md4c into a QTextDocument-compatible, palette-themed HTML representation, performs per-code-block KSyntaxHighlighting using fence languages, resolves local resources, preserves reading position across theme changes, and adds corresponding build dependencies and extensive unit coverage.

Sequence diagram for themed markdown rendering

sequenceDiagram
    participant Browser as MarkdownBrowser
    participant Renderer as MarkdownRenderer
    participant Builder as MarkdownHtmlBuilder
    participant Parser as md4c
    participant Document as QTextDocument
    participant Highlighter as MarkdownCodeHighlighter

    Browser->>Renderer: render(document, markdown, basePath, theme)
    Renderer->>Builder: build(markdown)
    Builder->>Parser: md_parse(...)
    Parser-->>Builder: HTML fragment and codeLanguages
    Builder-->>Renderer: HTML fragment
    Renderer->>Document: setHtml(html)
    Renderer->>Highlighter: highlightRun(blocks, language)
    Highlighter->>Document: mergeCharFormat(...)
Loading

Sequence diagram for theme changes preserving reading position

sequenceDiagram
    participant Browser as MarkdownBrowser
    participant ThemeHelper as DGuiApplicationHelper
    participant Renderer as MarkdownRenderer
    participant Document as QTextDocument
    participant ScrollBar as QScrollBar

    ThemeHelper-->>Browser: themeTypeChanged()
    Browser->>ScrollBar: value()
    Browser->>Renderer: render(document, markdown, basePath, theme)
    Renderer->>Document: setHtml(html)
    Browser->>ScrollBar: setValue(scrollPosition)
Loading

Flow diagram for fenced code block highlighting

flowchart TD
    A[md4c parses fenced code block] --> B[Capture fence language]
    B --> C[Import themed HTML into QTextDocument]
    C --> D[Find code block QTextBlock run]
    D --> E["definitionFor(language)"]
    E --> F[highlightLine]
    F --> G[QTextCursor.mergeCharFormat]
Loading

File-Level Changes

Change Details Files
Replaced Qt's unstyled Markdown import with a two-pass md4c-to-Qt-HTML rendering pipeline.
  • Parse GitHub-flavored Markdown with md4c and emit HTML/style attributes supported by QTextDocument.
  • Implement themed formatting for headings, paragraphs, lists, quotes, rules, tables, inline markup, links, images, and code blocks.
  • Add Qt-specific layout workarounds for heading sizing, pixel margins, quote/rule borders, nested lists, image sizing, and HTML escaping.
src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdownhtmlbuilder.cpp
src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdownhtmlbuilder.h
Added palette-driven theming and theme-aware document rebuilding while preserving preview context.
  • Derive text, surface, link, quote, table, and font tokens from DGuiApplicationHelper/DPalette with light/dark fallbacks.
  • Configure document metrics, fonts, wrapping, and image base URLs/search paths during rendering.
  • Re-render on themeTypeChanged and preserve scroll position for theme updates while resetting it for newly loaded content.
src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdowntheme.cpp
src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdowntheme.h
src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdownrenderer.cpp
src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdownrenderer.h
src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdownbrowser.cpp
src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdownbrowser.h
Added per-fenced-block syntax highlighting using KSyntaxHighlighting.
  • Capture fence language info strings from md4c and pair them with imported code-block runs in document order.
  • Resolve common aliases and case/whitespace variants to KSyntaxHighlighting definitions, including fallback filename lookup.
  • Apply syntax formats with AbstractHighlighter state continuity and QTextCursor character-format merging without changing code-block surfaces.
src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdowncodehighlighter.cpp
src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdowncodehighlighter.h
src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdownrenderer.cpp
Updated build dependencies and expanded automated coverage for rendering, theming, highlighting, resources, and scrolling.
  • Link the preview and unit test targets against md4c and KF6SyntaxHighlighting and enable dependency-aware source globbing.
  • Add libmd4c-dev to Debian build dependencies.
  • Cover HTML generation, image resolution/fallbacks, Markdown constructs, language aliases, theme derivation, rendering markers, and scroll behavior.
src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/CMakeLists.txt
autotests/apps/dde-file-manager-preview/markdown-preview/CMakeLists.txt
debian/control
debian/control.in
autotests/apps/dde-file-manager-preview/markdown-preview/test_markdownbrowser.cpp
autotests/apps/dde-file-manager-preview/markdown-preview/test_markdowncodehighlighter.cpp
autotests/apps/dde-file-manager-preview/markdown-preview/test_markdownhtmlbuilder.cpp
autotests/apps/dde-file-manager-preview/markdown-preview/test_markdownrenderer.cpp
autotests/apps/dde-file-manager-preview/markdown-preview/test_markdowntheme.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdownrenderer.cpp" line_range="88-89" />
<code_context>
+            continue;
+        }
+
+        QVector<QTextBlock> run;
+        while (block.isValid() && isCodeBlock(block, theme)) {
+            run.append(block);
+            block = block.next();
</code_context>
<issue_to_address>
**Adjacent fences share one language**

When two fenced code blocks with different languages are consecutive without a non-code block between them, `highlightCodeBlocks()` groups their code-formatted blocks into one run and advances `languageIndex` only once, so `highlightRun()` applies the first fence’s grammar to later fences and the preview shows incorrect highlighting.

Split runs at fence boundaries and advance the language index for each fenced block.

Also at `src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdownrenderer.cpp:94`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment on lines +88 to +89
QVector<QTextBlock> run;
while (block.isValid() && isCodeBlock(block, theme)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High · Adjacent fences share one language

When two fenced code blocks with different languages are consecutive without a non-code block between them, highlightCodeBlocks() groups their code-formatted blocks into one run and advances languageIndex only once, so highlightRun() applies the first fence’s grammar to later fences and the preview shows incorrect highlighting.

Split runs at fence boundaries and advance the language index for each fenced block.

Also at src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdownrenderer.cpp:94.

Prompt for AI agents
In `src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdownrenderer.cpp` at lines 88-89:

**Adjacent fences share one language**

When two fenced code blocks with different languages are consecutive without a non-code block between them, `highlightCodeBlocks()` groups their code-formatted blocks into one run and advances `languageIndex` only once, so `highlightRun()` applies the first fence’s grammar to later fences and the preview shows incorrect highlighting.

Split runs at fence boundaries and advance the language index for each fenced block.

Also at `src/apps/dde-file-manager-preview/pluginpreviews/markdown-preview/markdownrenderer.cpp:94`.

@github-actions

Copy link
Copy Markdown
  • 检测到敏感词unset变动
详情
    {
    "unset": {
        "autotests/apps/dde-file-manager-preview/markdown-preview/test_markdownrenderer.cpp": {
            "b": [
                "// Note that an unset QBrush still reports black, hence the style check."
            ]
        }
    }
}

1. The markdown preview only called QTextDocument::setMarkdown(), which
   parses CommonMark but produces no visual formatting at all: no heading
   scale, no spacing, no code surface, no table borders. Rendering quality
   was far below expectations
2. Parse with md4c (the parser Qt uses internally, so the dialect still
   matches) and emit the HTML subset QTextHtmlParser actually honours -
   the same round-trip format QTextDocument::toHtml() produces. Inline
   style attributes carry the theme
3. md4c exposes the fence info string that Qt's reader discards, so code
   blocks are re-coloured per block with KSyntaxHighlighting
   (AbstractHighlighter::highlightLine + QTextCursor::mergeCharFormat),
   giving each block its own language
4. All colours derive from DGuiApplicationHelper/DPalette and the document
   is rebuilt on themeTypeChanged, so light/dark switch keeps the reading
   position
5. Work around four QtHTML limits found by rendering to PNG: h1-h6 ignore
   font-size (headings are emitted as sized bold paragraphs), block
   margins only honour px (pt is dropped), blockquote cannot draw a left
   bar and hr cannot take a colour (both use a one cell table with a
   border), and nested lists need margin-left on every level
6. Add libmd4c-dev to Build-Depends in both debian/control and
   debian/control.in; it is pure C with Architecture: any and present for
   amd64/arm64/loong64/riscv64

Log: Rewrite markdown preview with md4c parsing, themed rich text layout
and per-block code syntax highlighting

Influence:
1. Open any .md file in the file manager preview panel and confirm
   headings, paragraphs, lists, quotes, tables, images and fenced code
   blocks are properly typeset
2. Confirm fenced code blocks are syntax highlighted according to their
   info string (```cpp, ```python, ```json and so on)
3. Switch the system theme between light and dark and confirm the preview
   recolours without jumping back to the top
4. Open a markdown file with relative image references and confirm the
   images resolve against the file directory
5. Run ut-markdown-preview and confirm all cases pass

feat: 重写 markdown 预览渲染管线

1. 原实现只调用了一句 QTextDocument::setMarkdown(),该接口只解析
   CommonMark 语法,完全不产生任何排版样式:没有标题字号阶梯、没有
   段间距、没有代码块底色、没有表格边框,观感很差
2. 改用 md4c 解析(Qt 内部 setMarkdown 用的就是它,方言保持一致),
   只生成 QtHtmlParser 真正支持的 HTML 子集——即
   QTextDocument::toHtml() 自身的往返格式,配色通过内联样式属性携带
3. md4c 能给出 Qt 读取器丢弃的 fence 语言信息,因此导入后按块用
   KSyntaxHighlighting 二次着色
   (AbstractHighlighter::highlightLine + QTextCursor::mergeCharFormat),
   每个代码块都能使用自己的语言定义
4. 配色全部从 DGuiApplicationHelper/DPalette 派生,themeTypeChanged
   时整体重渲染,明暗切换保留阅读位置
5. 通过离屏渲染 PNG 实测,绕开四处 QtHTML 限制:h1-h6 忽略字号
   (标题改为带字号的粗体段落)、块边距只认 px(pt 被丢弃)、
   blockquote 无法画左侧色条且 hr 无法设色(两者改用单格表格加边框)、
   嵌套列表每一层都要写 margin-left
6. 在 debian/control 与 debian/control.in 的 Build-Depends 中新增
   libmd4c-dev;该包为纯 C、Architecture: any,
   amd64/arm64/loong64/riscv64 均已提供

Log: 重写 markdown 预览,改用 md4c 解析、跟随主题的富文本排版,并支持
按代码块语言语法高亮

Influence:
1. 在文件管理器预览面板打开任意 .md 文件,确认标题、段落、列表、
   引用、表格、图片与代码块排版正常
2. 确认 fenced 代码块按其 info string 语法高亮(```cpp、```python、
   ```json 等)
3. 切换系统明暗主题,确认预览重新着色且不会跳回顶部
4. 打开含相对路径图片引用的 markdown 文件,确认图片按文件所在目录
   正确解析
5. 运行 ut-markdown-preview,确认全部用例通过
@Johnson-zs
Johnson-zs force-pushed the feat/markdown-preview-richtext branch from 5190171 to 81f860f Compare October 27, 2026 12:54
@github-actions

Copy link
Copy Markdown
  • 检测到敏感词unset变动
详情
    {
    "unset": {
        "autotests/apps/dde-file-manager-preview/markdown-preview/test_markdownrenderer.cpp": {
            "b": [
                "// Note that an unset QBrush still reports black, hence the style check."
            ]
        }
    }
}

@deepin-ci-robot

Copy link
Copy Markdown
Contributor

deepin pr auto review

🤖 AI 代码审查报告

总体评分: 100 分 (通过阈值: 70分)

Pass


📊 总体评价

项目 结果
审查结论 代码审查通过
评分详情 本次提交重写了 dde-file-manager 的 Markdown 预览渲染管线,引入 md4c 解析器与 KSyntaxHighlighting 代码高亮,新增 MarkdownTheme/MarkdownHtmlBuilder/ MarkdownCodeHighlighter/MarkdownRenderer 四个模块,替换原先直接调用 QTextDocument::setMarkdown 的简单实现。代码结构清晰、职责分离良好、注释充分、 单元测试覆盖全面(4 个新测试文件),未发现安全漏洞,四维度均通过。

🔍 详细分析

1. 语法逻辑 ✅

评价: 语法正确,逻辑清晰 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: []


2. 代码质量 ✅

评价: 代码结构清晰,注释完整 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: []


3. 代码性能 ✅

评价: 性能良好,资源使用合理 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: []


4. 代码安全 🔒

评价: 存在0个安全漏洞 ✅ 通过

🔐 发现 0 个安全漏洞

安全漏洞详情:
✅ 未发现安全漏洞

建议: []


💡 改进建议代码示例


本报告由 AI 代码审查工具自动生成

@Johnson-zs

Copy link
Copy Markdown
Contributor Author

/forcemerge

@deepin-bot

deepin-bot Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

This pr force merged! (status: blocked)

@deepin-bot
deepin-bot Bot merged commit 8c4e1d8 into linuxdeepin:master Oct 28, 2026
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants