Skip to main content

nargo_document/plugin/
container.rs

1//! 自定义容器插件
2//!
3//! 提供对 Markdown 中自定义容器的支持,包括 tip、warning、danger、info 等
4
5use crate::plugin::{DocumentPlugin, PluginContext, PluginMeta};
6use lazy_static::lazy_static;
7use regex::Regex;
8use std::collections::HashMap;
9
10lazy_static! {
11    /// 匹配自定义容器的正则表达式
12    static ref CONTAINER_RE: Regex = Regex::new(r":::(\w+)\s*\n([\s\S]*?)\n:::").unwrap();
13}
14
15/// 自定义容器配置
16#[derive(Debug, Clone)]
17pub struct ContainerConfig {
18    /// 内置容器列表
19    pub containers: Vec<String>,
20    /// 自定义容器配置
21    pub custom_containers: HashMap<String, ContainerOptions>,
22    /// 是否启用默认图标
23    pub default_icons: bool,
24}
25
26/// 容器选项
27#[derive(Debug, Clone)]
28pub struct ContainerOptions {
29    /// 容器标题
30    pub title: Option<String>,
31    /// 容器图标
32    pub icon: Option<String>,
33    /// 容器 CSS 类
34    pub class: Option<String>,
35}
36
37impl Default for ContainerConfig {
38    fn default() -> Self {
39        Self { containers: vec!["tip".to_string(), "warning".to_string(), "danger".to_string(), "info".to_string()], custom_containers: HashMap::new(), default_icons: true }
40    }
41}
42
43/// 自定义容器插件
44pub struct ContainerPlugin {
45    /// 插件元数据
46    meta: PluginMeta,
47    /// 容器配置
48    config: ContainerConfig,
49}
50
51impl ContainerPlugin {
52    /// 创建新的 Container 插件实例
53    pub fn new() -> Self {
54        Self { meta: PluginMeta::new("nargo-document-plugin-container".to_string(), "0.1.0".to_string(), "自定义容器插件,提供信息提示、警告提示等容器组件".to_string()), config: ContainerConfig::default() }
55    }
56
57    /// 创建带自定义配置的 Container 插件实例
58    ///
59    /// # Arguments
60    ///
61    /// * `config` - 自定义容器配置
62    pub fn with_config(config: ContainerConfig) -> Self {
63        Self { meta: PluginMeta::new("nargo-document-plugin-container".to_string(), "0.1.0".to_string(), "自定义容器插件,提供信息提示、警告提示等容器组件".to_string()), config }
64    }
65
66    /// 处理自定义容器,将 :::type ... ::: 替换为 <div class="container-type">...</div>
67    ///
68    /// # Arguments
69    ///
70    /// * `content` - 包含自定义容器的文本内容
71    ///
72    /// # Returns
73    ///
74    /// 替换后的文本内容
75    fn process_containers(&self, content: &str) -> String {
76        CONTAINER_RE
77            .replace_all(content, |caps: &regex::Captures| {
78                let container_type = &caps[1];
79                let inner_content = &caps[2];
80
81                let options = self.config.custom_containers.get(container_type);
82                let class_name = options.and_then(|o| o.class.clone()).unwrap_or_else(|| format!("container-{}", container_type));
83
84                let mut html = String::new();
85                html.push_str(&format!("<div class=\"{}\">", class_name));
86
87                if let Some(options) = options {
88                    if let Some(title) = &options.title {
89                        html.push_str(&format!("<div class=\"container-title\">{}</div>", title));
90                    }
91                }
92
93                html.push_str(&format!("<div class=\"container-content\">{}</div>", inner_content.trim()));
94                html.push_str("</div>");
95
96                html
97            })
98            .to_string()
99    }
100}
101
102impl Default for ContainerPlugin {
103    fn default() -> Self {
104        Self::new()
105    }
106}
107
108impl DocumentPlugin for ContainerPlugin {
109    /// 获取插件元数据
110    fn meta(&self) -> &PluginMeta {
111        &self.meta
112    }
113
114    /// 渲染前钩子,在 Markdown 解析后、HTML 渲染前处理自定义容器
115    ///
116    /// # Arguments
117    ///
118    /// * `context` - 插件上下文,包含文档内容等信息
119    ///
120    /// # Returns
121    ///
122    /// 处理后的插件上下文
123    fn before_render(&self, context: PluginContext) -> PluginContext {
124        let content = self.process_containers(&context.content);
125
126        PluginContext { content, frontmatter: context.frontmatter, path: context.path }
127    }
128}