#html #markup #scope #parse #bbcode #parser

bbscope

基于作用域规则的 BBCode 转 HTML,自动关闭标签,高度可扩展

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解析器实现

MIT 许可证

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&#x27;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&#x27;m just like <s> opening tons of <sup> tags?<br>Maybe I&#x27;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(),它仍然兼容,但如果您创建了自定义标签,整个系统将弃用,转而使用 ScopeInfoEmitScope 组合。
  • 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