12 个版本
0.2.0 | 2023年7月5日 |
---|---|
0.1.8 | 2023年3月30日 |
0.1.7 | 2023年2月21日 |
0.1.4 | 2022年12月22日 |
0.0.6 | 2022年12月13日 |
#1481 在 解析器实现
59KB
717 行
更多 BBCode 解析器吗?
是的!我需要高度可扩展、灵活的,并且具有作用域规则的库,以确保始终生成正确的 HTML。例如,有像这样的内容
[b]This is bold! [i]AND ITALIC?[/b] oops where's [/i]?
你有不匹配的关闭标签。虽然简单的正则表达式替换可以以多种方式处理这个问题,但这个库将生成
<b>This is bold! <i>AND ITALIC?</i></b> oops where's ?
另一个例子
And so [s] I'm just like [s] opening tons of [sup] tags?
Maybe I'll close this [/s] one
And so <s> I'm just like <s> opening tons of <sup> tags?<br>Maybe I'll close this </sup></s> one</s>
所有未关闭的标签都会自动以正确的顺序关闭,包括文本末尾留下的任何标签。不匹配的关闭标签将被删除。当然,这可能不是你想要的,但我发现大多数较老或成熟的 bbcode 解析器都是这样工作的。使用这个库,你(通常)可以放心,它将生成正确的 HTML。
使用作用域规则,你还可以通过指定 only
向量来访问可以拒绝其中其他标签的标签,稍后将有更多介绍。例如,在扩展标签集中,我有 [code] 这个标签拒绝所有类型的匹配,除了正常文本和“垃圾”(我们丢弃的字符,如 \r)。
快速开始
let bbcode = BBCode::default().unwrap(); // Or catch error
let html = bbcode.parse("[url]https://github.com[/url]")
// Make sure to reuse the BBCode you created! Creating is expensive, but cloning is cheap!
let bbcode2 = bbcode.clone();
或者,如果你想使用扩展集(下一节将列出)
// Instead of requesting the default BBCode parser, you can pass in a configuration
// for the most common alterations, along with a vector of extra tag parsers (see later)
let mut bbcode = BBCode::from_config(BBCodeTagConfig::extended(), None).unwrap();
let html = bbcode.parse("[code]wow\nthis is code![/code]");
或者,如果你想添加自己的标签
let mut config = BBCodeTagConfig::default(); // Default does not include extended tags fyi
let mut matchers = Vec<MatchInfo>::new(); // A list of NEW matchers we'll pass to from_config
// You define how your tag gets turned into HTML using a closure; you are provided the open tag
// regex capture, the pre-parsed pre-escaped body, and the closing tag regex capture (if the user provided it).
// "EmitScope" is just a fancy alias so you don't have to fuss with the complicated types
let color_emitter : EmitScope = Arc::new(|open_capture,body,_c| {
//NOTE: in production code, don't `unwrap` the named capture group, it might not exist!
let color = open_capture.unwrap().name("attr").unwrap().as_str();
format!(r#"<span style="color:{}">{}</span>"#, color, body)
});
BBCode::add_tagmatcher(&mut matchers, "color", ScopeInfo::basic(color_emitter), None, None)?;
//Repeat the emitter / add_tagmatcher above for each tag you want to add
let bbcode = BBCode::from_config(config, Some(matchers));
《BBCode::add_tagmatcher
方法使用比通常所需的更少的代码为你构建一个 bbcode 标签解析器,但你可以技术上手动构建自己的匹配器,它可以匹配几乎任何东西。现在,如果你只是尝试添加基本的 bbcode 标签,你会在上面的
- 第一个参数是要追加匹配器的列表(它添加多个项)。
- 第二个是 bbcode 标签的名称,全部小写(因此这将匹配 [color])
- 第三个是一个特殊的“ScopeInfo”结构体,但我们调用的是“基本”构造函数,仅传递一个boxed闭包而不是配置整个 ScopeInfo。
- 这个boxed闭包是一个所谓的
EmitScope
,这是你编写的闭包,它提供了打开标签的正则表达式捕获、预先 HTML 转义并预先解析的正文,以及关闭标签的正则表达式捕获,你可以使用这些捕获输出(发出)构建的 HTML。请注意,尽管打开标签几乎总是提供的,但关闭标签经常没有提供,特别是如果用户没有关闭他们的标签。不要依赖最后一个参数(示例中的_c
)存在 - 请注意,开始标签捕获有一个名为
attr
的命名组,它是 bbcode 标签中属性的值。例如,如果您有[url=http://whatever]abc[/url]
,匹配attr
将包含字符串http://whatever
(未经预转义,请小心!) - 最后两个参数是可选的,用于在开始和结束标签前后消耗空行。例如,如果您想消耗开始标签 之前 的第一行空行,以及结束标签 之后 的第一行空行,这两个参数可能看起来像
Some((1,0)), Some((0,1))
(这可能会在未来发生变化)
Rocket Web 示例
对于 Rust,有很多可供选择的 Web 框架,因此为每个框架提供一个示例可能有点困难。有人建议使用 Rocket,所以这里是一个 0.5.0_rc2 的示例
#[macro_use] extern crate rocket;
use rocket::response::content;
use bbscope::BBCode;
#[launch]
fn rocket() -> _ {
let bbcode = BBCode::default().unwrap();
rocket::build()
.mount("/", routes![ index ])
.manage(bbcode) //Add as state, you want to reuse the bbcode object!!
}
#[get("/")]
fn index(bbcode: &State<BBCode>) -> content::RawHtml<String> {
content::RawHtml(bbcode.parse("Hey, it's [b]bbcode[/b]! [i]Oops, [u]forgot to close[/i] a tag"))
}
默认支持的标签
BBCode 非常多样化,有很多奇特的标签和系统,没有人能达成一致,或者至少如果他们达成了,那也已经太晚了。这些是在 '基本' 集中支持的标签
- [b]粗体[/b]
- [i]斜体[/i]
- [s]删除线[/s]
- [u]下划线[/u]
- [sup]上标/sup
- [sub]下标/sub
- [url=link*]url/url (=link 属性可选)
- [img=link*]link/img(只需一个:属性或内部)
- [list][*]item[*]item2/list
其中一些可能不是标准的,您可能缺少一些您认为标准的!如果是这样,还有一个可选的扩展列表
- [quote=cite*]引用块/quote (=cite 属性可选)
- [code]字面量预格式文本/code
- [icode]字面量内联/icode
- [youtube]youtube 链接*/youtube(当前以链接形式渲染!)
- [h1]大标题[/h1]
- [h2]中标题[/h2]
- [h3]小标题[/h3]
- [anchor=name]some text linkable with #name/anchor
- [spoiler=name]some text to hide/spoiler
当然,HTML 字符在所有地方都被转义了: ', ", &, <, >
不在 url 或 img 标签内的 URL 将自动链接,或者至少会尽力尝试自动链接它们(效果可能因情况而异)
注意事项
- 输出删除 \r 并将 \n 替换为 <br>(除了一些具有有限范围的标签外)。如果 <br> 会导致问题,您可以使用
BBCode::from_config(config, None)
并传入一个配置,其中将newline_to_br
设置为 false - 性能不是主要关注点,尽管您可以通过启用
perf
功能(启用一些正则表达式优化,我的测试中提高了约4倍)来启用额外的性能功能。 - 许多规则是任意的,旨在复制我多年来使用的一个现有的bbcode解析器。
变更日志
- 0.0.6:针对条件编译的小型错误修复。
- 0.1.0:完全重写;如果您使用
BBCode::default()
或BBCode::basics()
和BBCode::extras()
,它仍然兼容,但如果您创建了自定义标签,整个系统将弃用,转而使用ScopeInfo
和EmitScope
组合。 - 0.1.1:针对强制同步 + 发送闭包的小型错误修复(因此bbcode可以跨线程使用)。
- 0.1.2:为“代码”段添加了类。
- 0.1.3:添加了将bbcode解析器转换为仅消费其作用域标签的能力。
- 0.1.4:添加了我看到的一些二级语法:
[tag tag=attribute]
- 0.1.5:意外上传啊。
- 0.1.6:为链接目标添加了小型配置(没有API更改,只有新函数)。
- 0.1.7:像大多数其他bbcode解析器一样,消费标题周围的某些换行符。
- 0.1.8:更新单点依赖项。
- 0.2.0:修复ScopeInfo可见性,更改一些默认设置(<br>而不是换行符),更改解析器设置的API。
依赖关系
~3–4.5MB
~71K SLoC