Skip to content

TextView

TextView 用于在 GPUI 中渲染格式化文本。它支持 Markdown、简单 HTML、文本选择、代码块操作,以及通过 Markdown 插件解析和渲染项目自定义语法。

标准实现现在位于 gpui-base;本模块保留兼容重导出和组件主题适配。仅使用 Base 时的设置、完整默认样式及可选语法高亮请参阅 GPUI Base TextView

TextView::selectable(true) 使用 gpui-base 提供的窗口级文本选择引擎。如果要让普通文本或自定义 renderer 参与同一选择,请参阅 GPUI Base Text Selection(英文)。

导入

rust
use gpui_component::text::{markdown, TextView};

用法

Markdown

只需要渲染 Markdown 时,可以使用 markdown helper:

rust
use gpui_component::text::markdown;

markdown("# Hello\n\nThis is **Markdown**.")
    .selectable(true)
    .scrollable(true)

如果需要稳定 id,也可以直接构造 TextView

rust
use gpui_component::text::TextView;

TextView::markdown("preview", markdown_source)
    .selectable(true)

HTML

rust
TextView::html("html-preview", "<strong>Hello</strong>")

Markdown 插件

使用 .plugin(...) 支持自定义 Markdown 格式。插件同时拥有解析和渲染逻辑,调用方只需要把它挂到 TextView 上:

rust
markdown(source)
    .plugin(TickerPlugin::new())

Markdown 插件实现 MarkdownPlugin

rust
use gpui::{App, IntoElement, ParentElement as _, Window};
use gpui_component::text::{
    markdown_ast, MarkdownNode, MarkdownParseContext, MarkdownPlugin,
};

struct TickerNode {
    symbol: String,
}

struct TickerPlugin;

impl TickerPlugin {
    fn new() -> Self {
        Self
    }
}

impl MarkdownPlugin for TickerPlugin {
    fn is_block(&self) -> bool {
        true
    }

    fn name(&self) -> &str {
        "ticker"
    }

    fn parse(
        &self,
        node: &markdown_ast::Node,
        cx: &MarkdownParseContext<'_>,
    ) -> Option<MarkdownNode> {
        let markdown_ast::Node::Paragraph(paragraph) = node else {
            return None;
        };
        let [markdown_ast::Node::Text(text)] = paragraph.children.as_slice() else {
            return None;
        };
        let symbol = text.value.strip_prefix('$')?;

        Some(
            MarkdownNode::new(
                "ticker",
                TickerNode {
                    symbol: symbol.to_string(),
                },
            )
            .text(format!("${symbol}"))
            .markdown(cx.node_source(node).unwrap_or(text.value.as_str())),
        )
    }

    fn render(
        &self,
        node: &MarkdownNode,
        _window: &mut Window,
        _cx: &mut App,
    ) -> impl IntoElement {
        let ticker = node.data::<TickerNode>().expect("ticker node data");

        gpui::div().child(format!("${}", ticker.symbol))
    }
}

然后挂到 Markdown TextView

rust
markdown("$AAPL.US")
    .plugin(TickerPlugin::new())

MarkdownNode

MarkdownNodeparserender 之间传递的中性数据结构。

rust
MarkdownNode::new("ticker", TickerNode { symbol })
    .text("$AAPL.US")
    .markdown("$AAPL.US")
  • name 是稳定的节点名称,用于匹配 renderer。
  • data 是 parser 产生的类型化数据,通过 node.data::<T>() 读取。
  • text 是纯文本表示,用于选择和未注册 renderer 时的回退渲染。
  • markdown 是 Markdown 表示,用于将文档重新序列化为 Markdown。

Block 插件

当前自定义 Markdown 渲染支持 block 插件。现在可注册的插件需要在 is_block() 中返回 true

rust
fn is_block(&self) -> bool {
    true
}

Inline 插件保留给未来的 TextView 支持。

代码块操作

可以为 Markdown 代码块渲染操作控件:

rust
markdown(source)
    .code_block_actions(|code_block, _window, _cx| {
        gpui::div().child(format!("Run {}", code_block.lang().unwrap_or_default()))
    })