[图片] 一款结构化的 Markdown 引擎,为未来而构建 千呼万唤始出来 犹抱琵琶半遮面 转轴拨弦三两声 未成曲调先有情 [图片] [图片] [图片] [图片] [图片] [图片] 简介 Lute 是一款结构化的 Markdown 引擎,完整实现了最新的 GFM/CommonMark 规范,对中文语境支持更好。 ️ ..

Lute 一款结构化的 Markdown 引擎,支持 Go 和 JavaScript

Lute

一款结构化的 Markdown 引擎,为未来而构建

千呼万唤始出来 犹抱琵琶半遮面
转轴拨弦三两声 未成曲调先有情





        

💡 简介

Lute 是一款结构化的 Markdown 引擎,完整实现了最新的 GFM/CommonMark 规范,对中文语境支持更好。

📽️ 背景

之前我一直在使用其他 Markdown 引擎,它们或多或少都有些“瑕疵”:

Lute 的目标是构建一个结构化的 Markdown 引擎,实现 GFM/CM 规范并对中文提供更好的支持。所谓的“结构化”指的是从输入的 MD 文本构建抽象语法树,通过操作树来进行 HTML 输出、原文格式化等。
实现规范是为了保证 Markdown 渲染不存在二义性,让同一份 Markdown 文本可以在实现规范的 Markdown 引擎处理后得到一样的结果,这一点非常重要。

实现规范的引擎并不多,我想试试看自己能不能写上一个,这也是 Lute 的动机之一。关于如何实现一个 Markdown 引擎,网上众说纷纭:

我赞同后者,因为正则确实太难维护而且运行效率较低。最重要的原因是符合 GFM/CM 规范的 Markdown 引擎的核心解析算法不可能用正则写出来,因为规范定义的规则实在是太复杂了。

最后,还有一个很重要的动机就是 B3log 开源社区需要一款自己的 Markdown 引擎:

✨ 特性

🗃 案例

🇨 🇳 中文语境优化

♍ 格式化

格式化功能可将“不整洁”的 Markdown 文本格式化为统一风格,在需要公共编辑的场景下,统一的排版风格能让大家更容易协作。

点此展开格式化示例。
Markdown 原文:
# ATX 标题也有可能需要格式化的 ##
一个简短的段落。

Setext 说实话我不喜欢 Setext 标题
----
0. 有序列表可以从 0 开始
0. 应该自增序号的
1.   对齐对齐对齐

我们再来看看另一个有序列表。
1. 没空行的情况下序号要从 1 开始才能打断段落开始一个新列表
3. 虽然乱序不影响渲染
2. 但是随意写序号容易引起误解

试下贴段代码:
```go
package main

import "fmt"

func main() {
  fmt.Println("Hello, 世界")
}
```
对了,缩进代码块建议换成围栏代码块:

    缩进代码块太隐晦了
    也没法指定编程语言,容易导致代码高亮失效
    所以建议大家用 ``` 围栏代码块
试下围栏代码块匹配场景:
````markdown
围栏代码块只要开头的 ` 和结束的 ` 数量匹配即可,这样可以实现在围栏代码块中显示围栏代码块:
```
这里只有 3 个 `,所以不会匹配markdown代码块结束
```
下面匹配到就真的结束了。
````
以上块级内容都挤在一坨了,插入合理的空行也很有必要。


但是过多的空行分段也不好啊,用来分段的话一个空行就够了。



接下来让我们试试稍微复杂点的场景,比如列表项包含多个段落的情况:
1. 列表项中的第一段

   这里是第二个段落,贴段代码:
   ```markdown
   要成为Markdown程序员并不容易,同理PPT架构师也是。
   注意代码块中的中西文间并没有插入空格。
   ```
   这里是最后一段了。
1. 整个有序列表是“松散”的:列表项内容要用 `<p>` 标签

最后,我们试下对 GFM 的格式化支持:

|表格列a|表格列b|       表格列c   |
:---           |:---------------:|--:
第1列开头不要竖线      |   第2列   |第3列结尾不要竖线
                                 ||这个表格看得我眼都花了|

**以上就是为什么我们需要Markdown Format,而且是带中西文自动空格的格式化。**

格式化后:

# ATX 标题也有可能需要格式化的

一个简短的段落。

## Setext 说实话我不喜欢 Setext 标题

0. 有序列表可以从 0 开始
1. 应该自增序号的
2. 对齐对齐对齐

我们再来看看另一个有序列表。

1. 没空行的情况下序号要从 1 开始才能打断段落开始一个新列表
2. 虽然乱序不影响渲染
3. 但是随意写序号容易引起误解

试下贴段代码:

```go
package main

import "fmt"

func main() {
  fmt.Println("Hello, 世界")
}
```

对了,缩进代码块建议换成围栏代码块:

```
缩进代码块太隐晦了
也没法指定编程语言,容易导致代码高亮失效
所以建议大家用 ``` 围栏代码块
```

试下围栏代码块匹配场景:

````markdown
围栏代码块只要开头的 ` 和结束的 ` 数量匹配即可,这样可以实现在围栏代码块中显示围栏代码块:
```
这里只有 3 个 `,所以不会匹配markdown代码块结束
```
下面匹配到就真的结束了。
````

以上块级内容都挤在一坨了,插入合理的空行也很有必要。

但是过多的空行分段也不好啊,用来分段的话一个空行就够了。

接下来让我们试试稍微复杂点的场景,比如列表项包含多个段落的情况:

1. 列表项中的第一段

   这里是第二个段落,贴段代码:

   ```markdown
   要成为Markdown程序员并不容易,同理PPT架构师也是。
   注意代码块中的中西文间并没有插入空格。
   ```

   这里是最后一段了。
2. 整个有序列表是“松散”的:列表项内容要用 `<p>` 标签

最后,我们试下对 GFM 的格式化支持:

|表格列 a|表格列 b|表格列 c|
|:---|:---:|---:|
|第 1 列开头不要竖线|第 2 列|第 3 列结尾不要竖线|
||这个表格看得我眼都花了||

**以上就是为什么我们需要 Markdown Format,而且是带中西文自动空格的格式化。**

✍️ 术语修正

Markdown 原文:

在github上做开源项目是一件很开心的事情,请不要把Github拼写成`github`哦!

特别是简历中千万不要出现这样的情况:

> 熟练使用JAVA、Javascript、GIT,对android、ios开发有一定了解,熟练使用Mysql、postgresql数据库。

修正后:

在 GitHub 上做开源项目是一件很开心的事情,请不要把 GitHub 拼写成`github`哦!

特别是简历中千万不要出现这样的情况:

> 熟练使用 Java、JavaScript、Git,对 Android、iOS 开发有一定了解,熟练使用 MySQL、PostgreSQL 数据库。

⚡ 性能

请看 Golang markdown 引擎性能基准测试

💪 健壮性

Lute 承载了黑客派上的所有 Markdown 处理,每天处理数十万请求,运行表现稳定。

🔒 安全性

Lute 没有实现实现 GFM 中的 Disallowed Raw HTML (extension),因为该扩展还是存在一定漏洞(比如没有处理 <input>)。
建议通过其他库(比如 bluemonday)来进行 HTML 安全过滤,这样也能更好地适配应用场景。

🛠️ 使用

有三种方式使用 Lute:

  1. 后端:用 Go 语言的话引入 github.com/88250/lute 包即可
  2. 后端:将 Lute 启动为一个 HTTP 服务进程供其他进程调用,具体请参考这里
  3. 前端:引入 js 目录下的 lute.min.js 即可,支持 Node.js

Go

引入 Lute 库:

go get -u github.com/88250/lute

最小化可工作示例:

package main

import (
	"fmt"

	"github.com/88250/lute"
)

func main() {
	luteEngine := lute.New() // 默认已经启用 GFM 支持以及中文语境优化
	html, err := luteEngine.MarkdownStr("demo", "**Lute** - A structured markdown engine.")
	if nil != err {
		panic(err)
	}
	fmt.Println(html)
	// <p><strong>Lute</strong> - A structured Markdown engine.</p>
}

关于代码块语法高亮:

JavaScript

简单示例可参考 JavaScript 目录下的 demo,结合前端编辑器的完整用法请参考 Vditor 中的示例

Vditor

一些细节:

  1. lute.js 没有内置语法高亮特性
  2. lute.js 编译后大小为 ~800KB,通过 brotli -o lute.min.js.br lute.min.js 压缩后大小 ~110KB,常规 GZip 压缩后大小 ~150KB

📜 文档

🏘️ 社区

📄 授权

Lute 使用 木兰宽松许可证, 第1版 开源协议。

🙏 鸣谢

💡 简介

Lute 是一款结构化的 Markdown 引擎,完整实现了最新的 GFM/CommonMark 规范,对中文语境支持更好。

📽️ 背景

之前我一直在使用其他 Markdown 引擎,它们或多或少都有些“瑕疵”:

Lute 的目标是构建一个结构化的 Markdown 引擎,实现 GFM/CM 规范并对中文提供更好的支持。所谓的“结构化”指的是从输入的 MD 文本构建抽象语法树,通过操作树来进行 HTML 输出、原文格式化等。
实现规范是为了保证 Markdown 渲染不存在二义性,让同一份 Markdown 文本可以在实现规范的 Markdown 引擎处理后得到一样的结果,这一点非常重要。

实现规范的引擎并不多,我想试试看自己能不能写上一个,这也是 Lute 的动机之一。关于如何实现一个 Markdown 引擎,网上众说纷纭:

我赞同后者,因为正则确实太难维护而且运行效率较低。最重要的原因是符合 GFM/CM 规范的 Markdown 引擎的核心解析算法不可能用正则写出来,因为规范定义的规则实在是太复杂了。

最后,还有一个很重要的动机就是 B3log 开源社区需要一款自己的 Markdown 引擎:

✨ 特性

🗃 案例

🇨 🇳 中文语境优化

♍ 格式化

格式化功能可将“不整洁”的 Markdown 文本格式化为统一风格,在需要公共编辑的场景下,统一的排版风格能让大家更容易协作。

点此展开格式化示例。
Markdown 原文:
# ATX 标题也有可能需要格式化的 ##
一个简短的段落。

Setext 说实话我不喜欢 Setext 标题
----
0. 有序列表可以从 0 开始
0. 应该自增序号的
1.   对齐对齐对齐

我们再来看看另一个有序列表。
1. 没空行的情况下序号要从 1 开始才能打断段落开始一个新列表
3. 虽然乱序不影响渲染
2. 但是随意写序号容易引起误解

试下贴段代码:
```go
package main

import "fmt"

func main() {
  fmt.Println("Hello, 世界")
}
```
对了,缩进代码块建议换成围栏代码块:

    缩进代码块太隐晦了
    也没法指定编程语言,容易导致代码高亮失效
    所以建议大家用 ``` 围栏代码块
试下围栏代码块匹配场景:
````markdown
围栏代码块只要开头的 ` 和结束的 ` 数量匹配即可,这样可以实现在围栏代码块中显示围栏代码块:
```
这里只有 3 个 `,所以不会匹配markdown代码块结束
```
下面匹配到就真的结束了。
````
以上块级内容都挤在一坨了,插入合理的空行也很有必要。


但是过多的空行分段也不好啊,用来分段的话一个空行就够了。



接下来让我们试试稍微复杂点的场景,比如列表项包含多个段落的情况:
1. 列表项中的第一段

   这里是第二个段落,贴段代码:
   ```markdown
   要成为Markdown程序员并不容易,同理PPT架构师也是。
   注意代码块中的中西文间并没有插入空格。
   ```
   这里是最后一段了。
1. 整个有序列表是“松散”的:列表项内容要用 `<p>` 标签

最后,我们试下对 GFM 的格式化支持:

|表格列a|表格列b|       表格列c   |
:---           |:---------------:|--:
第1列开头不要竖线      |   第2列   |第3列结尾不要竖线
                                 ||这个表格看得我眼都花了|

**以上就是为什么我们需要Markdown Format,而且是带中西文自动空格的格式化。**

格式化后:

# ATX 标题也有可能需要格式化的

一个简短的段落。

## Setext 说实话我不喜欢 Setext 标题

0. 有序列表可以从 0 开始
1. 应该自增序号的
2. 对齐对齐对齐

我们再来看看另一个有序列表。

1. 没空行的情况下序号要从 1 开始才能打断段落开始一个新列表
2. 虽然乱序不影响渲染
3. 但是随意写序号容易引起误解

试下贴段代码:

```go
package main

import "fmt"

func main() {
  fmt.Println("Hello, 世界")
}
```

对了,缩进代码块建议换成围栏代码块:

```
缩进代码块太隐晦了
也没法指定编程语言,容易导致代码高亮失效
所以建议大家用 ``` 围栏代码块
```

试下围栏代码块匹配场景:

````markdown
围栏代码块只要开头的 ` 和结束的 ` 数量匹配即可,这样可以实现在围栏代码块中显示围栏代码块:
```
这里只有 3 个 `,所以不会匹配markdown代码块结束
```
下面匹配到就真的结束了。
````

以上块级内容都挤在一坨了,插入合理的空行也很有必要。

但是过多的空行分段也不好啊,用来分段的话一个空行就够了。

接下来让我们试试稍微复杂点的场景,比如列表项包含多个段落的情况:

1. 列表项中的第一段

   这里是第二个段落,贴段代码:

   ```markdown
   要成为Markdown程序员并不容易,同理PPT架构师也是。
   注意代码块中的中西文间并没有插入空格。
   ```

   这里是最后一段了。
2. 整个有序列表是“松散”的:列表项内容要用 `<p>` 标签

最后,我们试下对 GFM 的格式化支持:

|表格列 a|表格列 b|表格列 c|
|:---|:---:|---:|
|第 1 列开头不要竖线|第 2 列|第 3 列结尾不要竖线|
||这个表格看得我眼都花了||

**以上就是为什么我们需要 Markdown Format,而且是带中西文自动空格的格式化。**

✍️ 术语修正

Markdown 原文:

在github上做开源项目是一件很开心的事情,请不要把Github拼写成`github`哦!

特别是简历中千万不要出现这样的情况:

> 熟练使用JAVA、Javascript、GIT,对android、ios开发有一定了解,熟练使用Mysql、postgresql数据库。

修正后:

在 GitHub 上做开源项目是一件很开心的事情,请不要把 GitHub 拼写成`github`哦!

特别是简历中千万不要出现这样的情况:

> 熟练使用 Java、JavaScript、Git,对 Android、iOS 开发有一定了解,熟练使用 MySQL、PostgreSQL 数据库。

⚡ 性能

请看 Golang markdown 引擎性能基准测试

💪 健壮性

Lute 承载了黑客派上的所有 Markdown 处理,每天处理数十万请求,运行表现稳定。

🔒 安全性

Lute 没有实现实现 GFM 中的 Disallowed Raw HTML (extension),因为该扩展还是存在一定漏洞(比如没有处理 <input>)。
建议通过其他库(比如 bluemonday)来进行 HTML 安全过滤,这样也能更好地适配应用场景。

🛠️ 使用

有三种方式使用 Lute:

  1. 后端:用 Go 语言的话引入 github.com/88250/lute 包即可
  2. 后端:将 Lute 启动为一个 HTTP 服务进程供其他进程调用,具体请参考这里
  3. 前端:引入 js 目录下的 lute.min.js 即可,支持 Node.js

Go

引入 Lute 库:

go get -u github.com/88250/lute

最小化可工作示例:

package main

import (
	"fmt"

	"github.com/88250/lute"
)

func main() {
	luteEngine := lute.New() // 默认已经启用 GFM 支持以及中文语境优化
	html, err := luteEngine.MarkdownStr("demo", "**Lute** - A structured markdown engine.")
	if nil != err {
		panic(err)
	}
	fmt.Println(html)
	// <p><strong>Lute</strong> - A structured Markdown engine.</p>
}

关于代码块语法高亮:

JavaScript

简单示例可参考 JavaScript 目录下的 demo,结合前端编辑器的完整用法请参考 Vditor 中的示例

Vditor

一些细节:

  1. lute.js 没有内置语法高亮特性
  2. lute.js 编译后大小为 ~800KB,通过 brotli -o lute.min.js.br lute.min.js 压缩后大小 ~110KB,常规 GZip 压缩后大小 ~150KB

📜 文档

🏘️ 社区

📄 授权

Lute 使用 木兰宽松许可证, 第1版 开源协议。

🙏 鸣谢

Lute 的诞生离不开以下开源项目,在此对这些项目的贡献者们致敬!

  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    5 引用 • 95 回帖 • 4 关注
  • Markdown

    Markdown 是一种可以使用普通文本编辑器编写的标记语言,通过类似 HTML 的标记语法,它可以使普通文本内容具有一定的格式,具体语法请参考 Markdown 教程

    94 引用 • 623 回帖 • 617 关注
  • CommonMark
    2 引用 • 37 回帖
  • 开源

    Open Source, Open Mind, Open Sight, Open Future!

    270 引用 • 2825 回帖 • 810 关注
6 操作
88250 在 2019-08-29 11:36:29 置顶了该帖
88250 在 2019-11-22 22:54:25 更新了该帖
88250 在 2019-11-24 12:48:41 更新了该帖
88250 在 2019-11-24 12:49:05 更新了该帖 88250 在 2019-11-25 10:38:12 更新了该帖 88250 在 2019-11-30 09:35:30 更新了该帖
31 回帖
请输入回帖内容...
  • InkDP 1

    作为第一个点 star 的表示牛逼

  • gitors 1

    作为第二个 start 表示很荣幸

  • lizhongyue248 1

    前不久开始都用 asciiDoc 来写了,感觉相比 markdown 强大好多啊,自定义程度会很高,就是可用库比较少。

  • csfwff 1

    作为第 ④ 个 star 表示 666

  • ferried 1 评论

    1 回复
    bug?
    gitors
  • dengwentong 1

    果然还是关注的及时 😄

  • gitors 1

    很好奇为什么 是为未来而构建呢? 难不成 别人都是为过去构建的吗?

    2 回复
  • 88250

    Lute 的终极目标是成为一款下一代的 Markdown 引擎。

    其他库也是为未来而构建的,大家的库都是为未来而构建的(小猪佩奇跳泥坑) 😂

  • InkDP

  • JssDream 1

    😏 👍

  • fei123 1

    先收藏再说。加油加油,早日达成目标。

  • lvtaos 1 评论

    收藏按钮非常隐蔽啊。

    使用习惯就好了,操作集中到一块其实更方便 : )
    88250
  • 2501224066 1

    TIM图片20190819154837.gif

  • wgh 1

    666, 这就已经实现了。真快啊

  • 996

    只支持 Golang? 有无其它语言的 SDK?

    1 回复
  • 88250

    目前仅支持 golang,后期可能会通过 WASM 支持 JavaScript。Lute 内置了一个简单的 HTTP Server,可以起独立进程专门处理 Markdown 渲染,方便服务端进行复用。

  • jeffjade

    话说,这些比较好的特性,如:

    • 自动链接识别加强
    • 在中西文间自动插入空格
    • 术语拼写修正

    会考虑也在 Vditor 中注入下么?

    1 回复
  • 88250

    考虑用 WASM 导出 JS SDK,理论上应该可行。

  • clipw

    SoloPipeSym 需要效果统一的 Markdown 渲染,并且性能非常重要

    Sym 中怎么用呢,好像默认的是第三方的引擎

    1 回复
  • 88250

    这三个项目都支持使用 HTTP Markdown 服务的,具体可看下已有代码,稍加改造即可使用。正式的改造我们已经做好计划了,后续会进行实现。

    1 回复
  • clipw
    展开

    好的,刚试了一下,直接用https://github.com/b3log/markdown-http也方便。有个报错

    [ERROR]-[2019-08-29 23:19:32]-[org.b3log.latke.servlet.renderer.AbstractFreeMarkerRenderer:131]: Renders template [tag-articles.ftl] failed [Request [
        method=GET,
        URL=http://www.test.com:443/tag/java/reply,
        contentType=null,
        characterEncoding=UTF-8,
        local=[
            addr=127.0.0.1,
            port=8080,
            name=127.0.0.1],
    
    
    FreeMarker template error:
    The following has evaluated to null or missing:
    ==> article.articleTagObjs  [in template "classic/common/list-item.ftl" at line 22, column 16]
    The following has evaluated to null or missing:
    ==> article.articleTagObjs  [in template "classic/common/list-item.ftl" at line 22, column 16]
    
    ----
    Tip: It's the step after the last dot that caused this error, not those before it.
    ----
    Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
    ----
    
    ----
    FTL stack trace ("~" means nesting-related):
    	- Failed at: #list article.articleTagObjs as artic...  [in template "classic/common/list-item.ftl" at line 22, column 9]
    	- Reached through: #include "common/list-item.ftl"  [in template "classic/macro-list.ftl" in macro "list" at line 28, column 9]
    	- Reached through: @list listData=articles  [in template "classic/tag-articles.ftl" at line 107, column 25]
    ----
    
    该回帖因偏离主题而被折叠
    3 操作
    88250 在 2019-08-29 23:59:50 折叠了该回帖
    csfwff 在 2019-08-30 09:05:02 取消折叠了该回帖
    csfwff 在 2019-08-30 09:05:22 折叠了该回帖
  • clipw
    展开

    Java stack trace (for programmers):

    freemarker.core.InvalidReferenceException: [... Exception message was already printed; see it above ...]
    at freemarker.core.InvalidReferenceException.getInstance(InvalidReferenceException.java:134)
    at freemarker.core.Expression.assertNonNull(Expression.java:233)
    at freemarker.core.IteratorBlock.acceptWithResult(IteratorBlock.java:103)
    at freemarker.core.IteratorBlock.accept(IteratorBlock.java:93)
    at freemarker.core.Environment.visit(Environment.java:330)
    at freemarker.core.Environment.visit(Environment.java:336)
    at freemarker.core.Environment.include(Environment.java:2582)
    at freemarker.core.Include.accept(Include.java:171)
    at freemarker.core.Environment.visit(Environment.java:366)
    at freemarker.core.IteratorBlockIterationContext.executedNestedContentForCollOrSeqListing(IteratorBlock.java:317) at freemarker.core.IteratorBlockIterationContext.executeNestedContent(IteratorBlock.java:271)
    at freemarker.core.IteratorBlockIterationContext.accept(IteratorBlock.java:242) at freemarker.core.Environment.visitIteratorBlock(Environment.java:642) at freemarker.core.IteratorBlock.acceptWithResult(IteratorBlock.java:107) at freemarker.core.IteratorBlock.accept(IteratorBlock.java:93) at freemarker.core.Environment.visit(Environment.java:366) at freemarker.core.Environment.invoke(Environment.java:775) at freemarker.core.UnifiedCall.accept(UnifiedCall.java:83) at freemarker.core.Environment.visit(Environment.java:330) at freemarker.core.Environment.visit(Environment.java:336) at freemarker.core.Environment.visit(Environment.java:336) at freemarker.core.Environment.process(Environment.java:309) at freemarker.template.Template.process(Template.java:384) at org.b3log.latke.servlet.renderer.AbstractFreeMarkerRenderer.genHTML(AbstractFreeMarkerRenderer.java:154) at org.b3log.symphony.processor.SkinRenderer.genHTML(SkinRenderer.java:137) at org.b3log.latke.servlet.renderer.AbstractFreeMarkerRenderer.render(AbstractFreeMarkerRenderer.java:125) at org.b3log.latke.servlet.DispatcherServlet.result(DispatcherServlet.java:118) at org.b3log.latke.servlet.DispatcherServlet.handle(DispatcherServlet.java:97) at org.b3log.latke.servlet.DispatcherServlet.service(DispatcherServlet.java:69) at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:844) at org.eclipse.jetty.servlet.ServletHandlerCachedChain.doFilter(ServletHandler.java:1604)
    at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:226)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1591)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:542)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:536)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1581)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1307)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:482)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1549)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1204)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)

    该回帖因偏离主题而被折叠
    1 操作
    88250 在 2019-08-29 23:59:55 折叠了该回帖
  • clipw
    展开

    at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:221)
    at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:146)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
    at org.eclipse.jetty.server.Server.handle(Server.java:494)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:374)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:268)
    at org.eclipse.jetty.io.AbstractConnectionReadCallback.succeeded(AbstractConnection.java:311) at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103) at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:129) at org.eclipse.jetty.util.thread.ReservedThreadExecutorReservedThread.run(ReservedThreadExecutor.java:367)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:782)
    at org.eclipse.jetty.util.thread.QueuedThreadPoolRunner.run(QueuedThreadPool.java:918) at java.lang.Thread.run(Thread.java:748) at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:221) at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:146) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) at org.eclipse.jetty.server.Server.handle(Server.java:494) at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:374) at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:268) at org.eclipse.jetty.io.AbstractConnectionReadCallback.succeeded(AbstractConnection.java:311)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
    at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:129)
    at org.eclipse.jetty.util.thread.ReservedThreadExecutorReservedThread.run(ReservedThreadExecutor.java:367) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:782) at org.eclipse.jetty.util.thread.QueuedThreadPoolRunner.run(QueuedThreadPool.java:918)
    at java.lang.Thread.run(Thread.java:748)

    该回帖因偏离主题而被折叠
    1 操作
    88250 在 2019-08-30 00:00:00 折叠了该回帖
  • clipw 1 评论
    展开

    sym 的报错,回复字数限制,日志贴了几个回帖 😄

    该回帖因偏离主题而被折叠
    1 操作
    88250 在 2019-08-30 00:00:24 折叠了该回帖
    Sym 的报错麻烦单独开贴或者到项目 Issues 列表上反馈,谢谢。
    88250
  • cuijianzhe

    这就有点钻牛角尖了啊 哈哈哈 😄

  • 88250 1 评论

    @participants Lute 的 Wasm 包基本可用了,这意味着可以在现代化的浏览器或者 Node.js 中直接通过 JavaScript 调用 Lute 了!浏览器使用方式可参考 wasm 目录下的 demo。

    目前主要问题是 Wasm 包的大小比较大(~650KB),正在尝试通过 TinyGo 对其进行重新编译,如果成功的话包可以小很多。

    欢迎大家对 Lute 献计献策和测试,谢谢!

    JavaScript 端技术方案由 TinyGo Wasm 调整为 GopherJS 了,已经可以生成 lute.js。生成的包压缩后大小 ~100KB,后续会继续优化包大小。
    88250
  • WOLFCHAN

    D 大也太高效了吧,这么快就做出来了,佩服,大佬就是大佬 仰望

    1 回复
  • 88250

    在前人工作的基础上稍微码了一下,不算啥成就。感谢支持,会继续完善的。

  • cuijianzhe

    从原来视频解析

    <center><video width="100%" height="100%" controls>
    <source src="https://cjz.cjzshilong.cn/sunbayi.mp4" 
     type="video/mp4">
    </video><center>
    

    变为:https://cjz.cjzshilong.cn/sunbayi.mp4

  • luckrill

    有意思,我开发的 Markdown 工具,可以参考看看 https://go.readmorejoy.com/markdown

    1 回复
  • 88250

    用 URL https://hacpai.com/article/1567047822949 尝试了一下,发现几个问题:

    1. 选择“兼容性好”返回空白

    2. 选择“速度快”,返回的内容有乱码

    3. 解析结果也不正确(参考 CommonMark Spec),比如对于 HTML:

      <code class="language-markdown">1. one
      
      2. two
      3. three
      </code>
      

      应该返回:

          1. one
      
          2. two
          3. three
      

      但你的工具返回:

          1. one
      
      2. two
      3. three
      
    4. 建议代码块解析统一使用“围栏代码块”(```lang),这样不会丢失语言注解

请输入回帖内容 ...