Skip to content

Commit

Permalink
🎨 将 Markdown 引擎选项拆分为解析选项和渲染选项 Vanessa219/vditor#868
Browse files Browse the repository at this point in the history
  • Loading branch information
88250 committed Dec 23, 2020
1 parent b8dfc16 commit b848c91
Show file tree
Hide file tree
Showing 47 changed files with 328 additions and 386 deletions.
2 changes: 1 addition & 1 deletion h2m.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (lute *Lute) HTML2Markdown(htmlStr string) (markdown string, err error) {

// 将 AST 进行 Markdown 格式化渲染
var formatted []byte
renderer := render.NewFormatRenderer(tree)
renderer := render.NewFormatRenderer(tree, render.NewOptions())
for nodeType, rendererFunc := range lute.HTML2MdRendererFuncs {
renderer.ExtRendererFuncs[nodeType] = rendererFunc
}
Expand Down
88 changes: 21 additions & 67 deletions lute.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ type Lute struct {
//
// 默认启用的解析选项:
// * GFM 支持
// * 软换行转硬换行
// * 脚注
// * 标题自定义 ID
// * 修正术语拼写
Expand All @@ -54,10 +53,11 @@ type Lute struct {
// * YAML Front Matter
//
// 默认启用的渲染选项:
// * 软换行转硬换行
// * 代码块语法高亮
// * 中西文间插入空格
func New(opts ...ParseOption) (ret *Lute) {
ret = &Lute{ParseOptions: NewParseOptions()}
ret = &Lute{ParseOptions: parse.NewOptions(), RenderOptions: render.NewOptions()}
for _, opt := range opts {
opt(ret)
}
Expand All @@ -75,54 +75,6 @@ func New(opts ...ParseOption) (ret *Lute) {
return ret
}

func NewRenderOptions() *render.Options {
return &render.Options{
AutoSpace: true,
RenderListStyle: false,
CodeSyntaxHighlight: true,
CodeSyntaxHighlightInlineStyle: false,
CodeSyntaxHighlightLineNum: false,
CodeSyntaxHighlightStyleName: "github",
VditorWYSIWYG: false,
VditorIR: false,
VditorSV: false,
KramdownIAL: false,
ChineseParagraphBeginningSpace: false,
}
}

func NewParseOptions() *parse.ParseOptions {
emojis, emoji := parse.NewEmojis()
return &parse.ParseOptions{
GFMTable: true,
GFMTaskListItem: true,
GFMTaskListItemClass: "vditor-task",
GFMStrikethrough: true,
GFMAutoLink: true,
SoftBreak2HardBreak: true,
Footnotes: true,
ToC: false,
HeadingID: true,
FixTermTypo: true,
ChinesePunct: true,
Emoji: true,
AliasEmoji: emojis,
EmojiAlias: emoji,
Terms: render.NewTerms(),
EmojiSite: "https://cdn.jsdelivr.net/npm/vditor/dist/images/emoji",
LinkBase: "",
LinkPrefix: "",
VditorCodeBlockPreview: true,
VditorMathBlockPreview: true,
VditorHTMLBlockPreview: true,
YamlFrontMatter: true,
BlockRef: false,
Mark: false,
KramdownIAL: false,
KramdownIALIDRenderName: "id",
}
}

// Markdown 将 markdown 文本字节数组处理为相应的 html 字节数组。name 参数仅用于标识文本,比如可传入 id 或者标题,也可以传入 ""。
func (lute *Lute) Markdown(name string, markdown []byte) (html []byte) {
tree := parse.Parse(name, markdown, lute.ParseOptions)
Expand Down Expand Up @@ -159,7 +111,7 @@ func (lute *Lute) FormatStr(name, markdown string) (formatted string) {
// TextBundle 将 markdown 文本字节数组进行 TextBundle 处理。
func (lute *Lute) TextBundle(name string, markdown []byte, linkPrefixes []string) (textbundle []byte, originalLinks []string) {
tree := parse.Parse(name, markdown, lute.ParseOptions)
renderer := render.NewTextBundleRenderer(tree, linkPrefixes)
renderer := render.NewTextBundleRenderer(tree, linkPrefixes, lute.RenderOptions)
textbundle, originalLinks = renderer.Render()
return
}
Expand Down Expand Up @@ -233,13 +185,13 @@ func (lute *Lute) RemoveEmoji(str string) string {

// GetTerms 返回术语字典。
func (lute *Lute) GetTerms() map[string]string {
return lute.ParseOptions.Terms
return lute.RenderOptions.Terms
}

// PutTerms 将制定的 termMap 合并覆盖已有的术语字典。
func (lute *Lute) PutTerms(termMap map[string]string) {
for k, v := range termMap {
lute.ParseOptions.Terms[k] = v
lute.RenderOptions.Terms[k] = v
}
}

Expand Down Expand Up @@ -273,7 +225,7 @@ func (lute *Lute) SetGFMTaskListItem(b bool) {
}

func (lute *Lute) SetGFMTaskListItemClass(class string) {
lute.ParseOptions.GFMTaskListItemClass = class
lute.RenderOptions.GFMTaskListItemClass = class
}

func (lute *Lute) SetGFMStrikethrough(b bool) {
Expand All @@ -285,7 +237,7 @@ func (lute *Lute) SetGFMAutoLink(b bool) {
}

func (lute *Lute) SetSoftBreak2HardBreak(b bool) {
lute.ParseOptions.SoftBreak2HardBreak = b
lute.RenderOptions.SoftBreak2HardBreak = b
}

func (lute *Lute) SetCodeSyntaxHighlight(b bool) {
Expand Down Expand Up @@ -314,22 +266,24 @@ func (lute *Lute) SetFootnotes(b bool) {

func (lute *Lute) SetToC(b bool) {
lute.ParseOptions.ToC = b
lute.RenderOptions.ToC = b
}

func (lute *Lute) SetHeadingID(b bool) {
lute.ParseOptions.HeadingID = b
lute.RenderOptions.HeadingID = b
}

func (lute *Lute) SetAutoSpace(b bool) {
lute.RenderOptions.AutoSpace = b
}

func (lute *Lute) SetFixTermTypo(b bool) {
lute.ParseOptions.FixTermTypo = b
lute.RenderOptions.FixTermTypo = b
}

func (lute *Lute) SetChinesePunct(b bool) {
lute.ParseOptions.ChinesePunct = b
lute.RenderOptions.ChinesePunct = b
}

func (lute *Lute) SetEmoji(b bool) {
Expand All @@ -345,11 +299,11 @@ func (lute *Lute) SetEmojiSite(emojiSite string) {
}

func (lute *Lute) SetHeadingAnchor(b bool) {
lute.ParseOptions.HeadingAnchor = b
lute.RenderOptions.HeadingAnchor = b
}

func (lute *Lute) SetTerms(terms map[string]string) {
lute.ParseOptions.Terms = terms
lute.RenderOptions.Terms = terms
}

func (lute *Lute) SetVditorWYSIWYG(b bool) {
Expand All @@ -372,35 +326,35 @@ func (lute *Lute) SetInlineMathAllowDigitAfterOpenMarker(b bool) {
}

func (lute *Lute) SetLinkPrefix(linkPrefix string) {
lute.ParseOptions.LinkPrefix = linkPrefix
lute.RenderOptions.LinkPrefix = linkPrefix
}

func (lute *Lute) SetLinkBase(linkBase string) {
lute.ParseOptions.LinkBase = linkBase
lute.RenderOptions.LinkBase = linkBase
}

func (lute *Lute) GetLinkBase() string {
return lute.ParseOptions.LinkBase
return lute.RenderOptions.LinkBase
}

func (lute *Lute) SetVditorCodeBlockPreview(b bool) {
lute.ParseOptions.VditorCodeBlockPreview = b
lute.RenderOptions.VditorCodeBlockPreview = b
}

func (lute *Lute) SetVditorMathBlockPreview(b bool) {
lute.ParseOptions.VditorMathBlockPreview = b
lute.RenderOptions.VditorMathBlockPreview = b
}

func (lute *Lute) SetVditorHTMLBlockPreview(b bool) {
lute.ParseOptions.VditorHTMLBlockPreview = b
lute.RenderOptions.VditorHTMLBlockPreview = b
}

func (lute *Lute) SetRenderListStyle(b bool) {
lute.RenderOptions.RenderListStyle = b
}

func (lute *Lute) SetSanitize(b bool) {
lute.ParseOptions.Sanitize = b
lute.RenderOptions.Sanitize = b
}

func (lute *Lute) SetImageLazyLoading(dataSrc string) {
Expand Down Expand Up @@ -429,7 +383,7 @@ func (lute *Lute) SetKramdownIAL(b bool) {
}

func (lute *Lute) SetKramdownIALIDRenderName(name string) {
lute.ParseOptions.KramdownIALIDRenderName = name
lute.RenderOptions.KramdownIALIDRenderName = name
}

func (lute *Lute) SetTag(b bool) {
Expand Down
50 changes: 0 additions & 50 deletions parse/inline_link.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
package parse

import (
"bytes"
"github.com/88250/lute/html"
"unicode/utf8"

Expand Down Expand Up @@ -148,52 +147,3 @@ func (context *Context) parseInlineLinkDest(tokens []byte) (passed, remains, des
}
return
}

func (context *Context) LinkPath(dest []byte) []byte {
dest = context.RelativePath(dest)
dest = context.PrefixPath(dest)
return dest
}

func (context *Context) PrefixPath(dest []byte) []byte {
if "" == context.ParseOption.LinkPrefix {
return dest
}

linkPrefix := util.StrToBytes(context.ParseOption.LinkPrefix)
ret := append(linkPrefix, dest...)
return ret
}

func (context *Context) RelativePath(dest []byte) []byte {
if "" == context.ParseOption.LinkBase {
return dest
}

// 强制将 %5C 即反斜杠 \ 转换为斜杠 / 以兼容 Windows 平台上使用的路径
dest = bytes.ReplaceAll(dest, []byte("%5C"), []byte("\\"))
if !context.isRelativePath(dest) {
return dest
}

linkBase := util.StrToBytes(context.ParseOption.LinkBase)
if !bytes.HasSuffix(linkBase, []byte("/")) {
linkBase = append(linkBase, []byte("/")...)
}
ret := append(linkBase, dest...)
if bytes.Equal(linkBase, ret) {
return []byte("")
}
return ret
}

func (context *Context) isRelativePath(dest []byte) bool {
if 1 > len(dest) {
return true
}

if '/' == dest[0] {
return false
}
return !bytes.Contains(dest, []byte(":/")) && !bytes.Contains(dest, []byte(":\\"))
}
56 changes: 22 additions & 34 deletions parse/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,26 +242,16 @@ type ParseOptions struct {
GFMTable bool
// GFMTaskListItem 设置是否打开“GFM 任务列表项”支持。
GFMTaskListItem bool
// GFMTaskListItemClass 作为 GFM 任务列表项类名,默认为 "vditor-task"。
GFMTaskListItemClass string
// GFMStrikethrough 设置是否打开“GFM 删除线”支持。
GFMStrikethrough bool
// GFMAutoLink 设置是否打开“GFM 自动链接”支持。
GFMAutoLink bool
// SoftBreak2HardBreak 设置是否将软换行(\n)渲染为硬换行(<br />)。
SoftBreak2HardBreak bool
// Footnotes 设置是否打开“脚注”支持。
Footnotes bool
// ToC 设置是否打开“目录”支持。
ToC bool
// HeadingID 设置是否打开“自定义标题 ID”支持。
HeadingID bool
// FixTermTypo 设置是否对普通文本中出现的术语进行修正。
// https://github.com/sparanoid/chinese-copywriting-guidelines
// 注意:开启术语修正的话会默认在中西文之间插入空格。
FixTermTypo bool
// ChinesePunct 设置是否对普通文本中出现中文后跟英文逗号句号等标点替换为中文对应标点。
ChinesePunct bool
// ToC 设置是否打开“目录”支持。
ToC bool
// Emoji 设置是否对 Emoji 别名替换为原生 Unicode 字符。
Emoji bool
// AliasEmoji 存储 ASCII 别名到表情 Unicode 映射。
Expand All @@ -270,10 +260,6 @@ type ParseOptions struct {
EmojiAlias map[string]string
// EmojiSite 设置图片 Emoji URL 的路径前缀。
EmojiSite string
// HeadingAnchor 设置是否对标题生成链接锚点。
HeadingAnchor bool
// Terms 将传入的 terms 合并覆盖到已有的 Terms 字典。
Terms map[string]string
// Vditor 所见即所得支持。
VditorWYSIWYG bool
// Vditor 即时渲染支持。
Expand All @@ -282,23 +268,8 @@ type ParseOptions struct {
VditorSV bool
// InlineMathAllowDigitAfterOpenMarker 设置内联数学公式是否允许起始 $ 后紧跟数字 https://github.com/b3log/lute/issues/38
InlineMathAllowDigitAfterOpenMarker bool
// LinkBase 设置链接、图片的基础路径。如果用户在链接或者图片地址中使用相对路径(没有协议前缀且不以 / 开头)并且 LinkBase 不为空则会用该值作为前缀。
// 比如 LinkBase 设置为 http://domain.com/,对于 ![foo](bar.png) 则渲染为 <img src="http://domain.com/bar.png" alt="foo" />
LinkBase string
// LinkPrefix 设置连接、图片的路径前缀。一旦设置该值,链接渲染将强制添加该值作为链接前缀,这有别于 LinkBase。
// 比如 LinkPrefix 设置为 http://domain.com,对于使用绝对路径的 ![foo](/local/path/bar.png) 则渲染为 <img src="http://domain.com/local/path/bar.png" alt="foo" />;
// 在 LinkBase 和 LinkPrefix 同时设置的情况下,会先处理 LinkBase 逻辑,最后再在 LinkBase 处理结果上加上 LinkPrefix。
LinkPrefix string
// VditorCodeBlockPreview 设置 Vditor 代码块是否需要渲染预览部分
VditorCodeBlockPreview bool
// VditorMathBlockPreview 设置 Vditor 数学公式块是否需要渲染预览部分
VditorMathBlockPreview bool
// VditorHTMLBlockPreview 设置 Vditor HTML 块是否需要渲染预览部分
VditorHTMLBlockPreview bool
// Setext 设置是否解析 Setext 标题 https://github.com/88250/lute/issues/50
Setext bool
// Sanitize 设置是否启用 XSS 安全过滤 https://github.com/88250/lute/issues/51
Sanitize bool
// YamlFrontMatter 设置是否开启 YAML Front Matter 支持。
YamlFrontMatter bool
// BlockRef 设置是否开启内容块引用支持。
Expand All @@ -307,9 +278,6 @@ type ParseOptions struct {
Mark bool
// KramdownIAL 设置是否打开 kramdown 内联属性列表支持。 https://kramdown.gettalong.org/syntax.html#inline-attribute-lists
KramdownIAL bool
// KramdownIALIDRenderName 设置 kramdown 内联属性列表中出现 id 属性时渲染 id 属性用的 name(key) 名称,默认为 "id"。
// 仅在 HTML 渲染器 HtmlRenderer 中支持。
KramdownIALIDRenderName string
// Tag 设置是否开启 #标签# 支持。
Tag bool
// ImgPathAllowSpace 设置是否支持图片路径带空格。
Expand All @@ -322,6 +290,26 @@ type ParseOptions struct {
Sub bool
}

func NewOptions() *ParseOptions {
emojis, emoji := NewEmojis()
return &ParseOptions{
GFMTable: true,
GFMTaskListItem: true,
GFMStrikethrough: true,
GFMAutoLink: true,
Footnotes: true,
Emoji: true,
AliasEmoji: emojis,
EmojiAlias: emoji,
EmojiSite: "https://cdn.jsdelivr.net/npm/vditor/dist/images/emoji",
YamlFrontMatter: true,
BlockRef: false,
Mark: false,
KramdownIAL: false,
HeadingID: true,
}
}

func (context *Context) ParentTip() {
if tip := context.Tip.Parent; nil != tip {
context.Tip = context.Tip.Parent
Expand Down
Loading

0 comments on commit b848c91

Please sign in to comment.