Skip to content

Commit

Permalink
🎨 Protyle Markdown syntax switch no longer affects clip/paste HTML pa…
Browse files Browse the repository at this point in the history
  • Loading branch information
88250 committed Oct 29, 2024
1 parent 91bbe8c commit 982dbd9
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 43 deletions.
6 changes: 6 additions & 0 deletions ast/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -1128,5 +1128,11 @@ const (

NodeCustomBlock NodeType = 560 // 自定义块

// HTML 标签,在无法使用 Markdown 标记符的情况下直接使用 HTML 标签

NodeHTMLTag NodeType = 570 // HTML 标签
NodeHTMLTagOpen NodeType = 571 // 开始 HTML 标签
NodeHTMLTagClose NodeType = 572 // 结束 HTML 标签

NodeTypeMaxVal NodeType = 1024 // 节点类型最大值
)
10 changes: 8 additions & 2 deletions ast/nodetype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

107 changes: 81 additions & 26 deletions h2m.go
Original file line number Diff line number Diff line change
Expand Up @@ -599,9 +599,13 @@ func (lute *Lute) genASTByDOM(n *html.Node, tree *parse.Tree) {
tree.Context.Tip.AppendChild(&ast.Node{Type: ast.NodeText, Tokens: util.StrToBytes(editor.Zwsp)})
}

node.Type = ast.NodeEmphasis
marker := "*"
node.AppendChild(&ast.Node{Type: ast.NodeEmA6kOpenMarker, Tokens: util.StrToBytes(marker)})
if !lute.ParseOptions.InlineAsterisk && !lute.ParseOptions.InlineUnderscore {
node.Type = ast.NodeHTMLTag
node.AppendChild(&ast.Node{Type: ast.NodeHTMLTagOpen, Tokens: util.StrToBytes("<em>")})
} else {
node.Type = ast.NodeEmphasis
node.AppendChild(&ast.Node{Type: ast.NodeEmA6kOpenMarker, Tokens: util.StrToBytes("*")})
}
tree.Context.Tip.AppendChild(node)
tree.Context.Tip = node
defer tree.Context.ParentTip()
Expand All @@ -619,9 +623,13 @@ func (lute *Lute) genASTByDOM(n *html.Node, tree *parse.Tree) {
tree.Context.Tip.AppendChild(&ast.Node{Type: ast.NodeText, Tokens: util.StrToBytes(editor.Zwsp)})
}

node.Type = ast.NodeStrong
marker := "**"
node.AppendChild(&ast.Node{Type: ast.NodeStrongA6kOpenMarker, Tokens: util.StrToBytes(marker)})
if !lute.ParseOptions.InlineAsterisk && !lute.ParseOptions.InlineUnderscore {
node.Type = ast.NodeHTMLTag
node.AppendChild(&ast.Node{Type: ast.NodeHTMLTagOpen, Tokens: util.StrToBytes("<strong>")})
} else {
node.Type = ast.NodeStrong
node.AppendChild(&ast.Node{Type: ast.NodeStrongA6kOpenMarker, Tokens: util.StrToBytes("**")})
}
tree.Context.Tip.AppendChild(node)
tree.Context.Tip = node
defer tree.Context.ParentTip()
Expand Down Expand Up @@ -813,28 +821,52 @@ func (lute *Lute) genASTByDOM(n *html.Node, tree *parse.Tree) {
}
}
case atom.Del, atom.S, atom.Strike:
node.Type = ast.NodeStrikethrough
marker := "~~"
node.AppendChild(&ast.Node{Type: ast.NodeStrikethrough2OpenMarker, Tokens: util.StrToBytes(marker)})
if !lute.ParseOptions.GFMStrikethrough {
node.Type = ast.NodeHTMLTag
node.AppendChild(&ast.Node{Type: ast.NodeHTMLTagOpen, Tokens: util.StrToBytes("<s>")})
} else {
node.Type = ast.NodeStrikethrough
node.AppendChild(&ast.Node{Type: ast.NodeStrikethrough2OpenMarker, Tokens: util.StrToBytes("~~")})
}
tree.Context.Tip.AppendChild(node)
tree.Context.Tip = node
defer tree.Context.ParentTip()
case atom.U:
node.Type = ast.NodeHTMLTag
node.AppendChild(&ast.Node{Type: ast.NodeHTMLTagOpen, Tokens: util.StrToBytes("<u>")})
tree.Context.Tip.AppendChild(node)
tree.Context.Tip = node
defer tree.Context.ParentTip()
case atom.Mark:
node.Type = ast.NodeMark
marker := "=="
node.AppendChild(&ast.Node{Type: ast.NodeMark2OpenMarker, Tokens: util.StrToBytes(marker)})
if !lute.ParseOptions.Mark {
node.Type = ast.NodeHTMLTag
node.AppendChild(&ast.Node{Type: ast.NodeHTMLTagOpen, Tokens: util.StrToBytes("<mark>")})
} else {
node.Type = ast.NodeMark
node.AppendChild(&ast.Node{Type: ast.NodeMark2OpenMarker, Tokens: util.StrToBytes("==")})
}
tree.Context.Tip.AppendChild(node)
tree.Context.Tip = node
defer tree.Context.ParentTip()
case atom.Sup:
node.Type = ast.NodeSup
node.AppendChild(&ast.Node{Type: ast.NodeSupOpenMarker})
if !lute.ParseOptions.Sup {
node.Type = ast.NodeHTMLTag
node.AppendChild(&ast.Node{Type: ast.NodeHTMLTagOpen, Tokens: util.StrToBytes("<sup>")})
} else {
node.Type = ast.NodeSup
node.AppendChild(&ast.Node{Type: ast.NodeSupOpenMarker})
}
tree.Context.Tip.AppendChild(node)
tree.Context.Tip = node
defer tree.Context.ParentTip()
case atom.Sub:
node.Type = ast.NodeSub
node.AppendChild(&ast.Node{Type: ast.NodeSubOpenMarker})
if !lute.ParseOptions.Sub {
node.Type = ast.NodeHTMLTag
node.AppendChild(&ast.Node{Type: ast.NodeHTMLTagOpen, Tokens: util.StrToBytes("<sub>")})
} else {
node.Type = ast.NodeSub
node.AppendChild(&ast.Node{Type: ast.NodeSubOpenMarker})
}
tree.Context.Tip.AppendChild(node)
tree.Context.Tip = node
defer tree.Context.ParentTip()
Expand Down Expand Up @@ -1214,12 +1246,19 @@ func (lute *Lute) genASTByDOM(n *html.Node, tree *parse.Tree) {

switch n.DataAtom {
case atom.Em, atom.I:
marker := "*"
node.AppendChild(&ast.Node{Type: ast.NodeEmA6kCloseMarker, Tokens: util.StrToBytes(marker)})
if !lute.ParseOptions.InlineAsterisk && !lute.ParseOptions.InlineUnderscore {
node.AppendChild(&ast.Node{Type: ast.NodeHTMLTagClose, Tokens: util.StrToBytes("</em>")})
} else {
node.Type = ast.NodeEmphasis
node.AppendChild(&ast.Node{Type: ast.NodeEmA6kCloseMarker, Tokens: util.StrToBytes("*")})
}
appendSpace(n, tree, lute)
case atom.Strong, atom.B:
marker := "**"
node.AppendChild(&ast.Node{Type: ast.NodeStrongA6kCloseMarker, Tokens: util.StrToBytes(marker)})
if !lute.ParseOptions.InlineAsterisk && !lute.ParseOptions.InlineUnderscore {
node.AppendChild(&ast.Node{Type: ast.NodeHTMLTagClose, Tokens: util.StrToBytes("</strong>")})
} else {
node.AppendChild(&ast.Node{Type: ast.NodeStrongA6kCloseMarker, Tokens: util.StrToBytes("**")})
}
appendSpace(n, tree, lute)
case atom.A:
node.AppendChild(&ast.Node{Type: ast.NodeCloseBracket})
Expand All @@ -1232,18 +1271,34 @@ func (lute *Lute) genASTByDOM(n *html.Node, tree *parse.Tree) {
}
node.AppendChild(&ast.Node{Type: ast.NodeCloseParen})
case atom.Del, atom.S, atom.Strike:
marker := "~~"
node.AppendChild(&ast.Node{Type: ast.NodeStrikethrough2CloseMarker, Tokens: util.StrToBytes(marker)})
if !lute.ParseOptions.GFMStrikethrough {
node.AppendChild(&ast.Node{Type: ast.NodeHTMLTagClose, Tokens: util.StrToBytes("</s>")})
} else {
node.AppendChild(&ast.Node{Type: ast.NodeStrikethrough2CloseMarker, Tokens: util.StrToBytes("~~")})
}
appendSpace(n, tree, lute)
case atom.U:
node.AppendChild(&ast.Node{Type: ast.NodeHTMLTagClose, Tokens: util.StrToBytes("</u>")})
case atom.Mark:
marker := "=="
node.AppendChild(&ast.Node{Type: ast.NodeMark2CloseMarker, Tokens: util.StrToBytes(marker)})
if !lute.ParseOptions.Mark {
node.AppendChild(&ast.Node{Type: ast.NodeHTMLTagClose, Tokens: util.StrToBytes("</mark>")})
} else {
node.AppendChild(&ast.Node{Type: ast.NodeMark2CloseMarker, Tokens: util.StrToBytes("==")})
}
appendSpace(n, tree, lute)
case atom.Sup:
node.AppendChild(&ast.Node{Type: ast.NodeSupCloseMarker})
if !lute.ParseOptions.Sup {
node.AppendChild(&ast.Node{Type: ast.NodeHTMLTagClose, Tokens: util.StrToBytes("</sup>")})
} else {
node.AppendChild(&ast.Node{Type: ast.NodeSupCloseMarker})
}
appendSpace(n, tree, lute)
case atom.Sub:
node.AppendChild(&ast.Node{Type: ast.NodeSubCloseMarker})
if !lute.ParseOptions.Sub {
node.AppendChild(&ast.Node{Type: ast.NodeHTMLTagClose, Tokens: util.StrToBytes("</sub>")})
} else {
node.AppendChild(&ast.Node{Type: ast.NodeSubCloseMarker})
}
appendSpace(n, tree, lute)
case atom.Details:
tree.Context.ParentTip()
Expand Down
8 changes: 4 additions & 4 deletions javascript/lute.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion javascript/lute.min.js.map

Large diffs are not rendered by default.

56 changes: 49 additions & 7 deletions parse/inline_html.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,25 +121,49 @@ func (t *Tree) parseInlineHTML(ctx *InlineContext) (ret *ast.Node) {
}
ctx.pos += len(tags)

if t.Context.ParseOption.ProtyleWYSIWYG {
if t.Context.ParseOption.ProtyleWYSIWYG && nil == ctx.brackets /* 方括号后跟标签的情况不处理 [<strong> */ {
if bytes.EqualFold(tags, []byte("<br />")) || bytes.EqualFold(tags, []byte("<br/>")) || bytes.EqualFold(tags, []byte("<br>")) {
ret = &ast.Node{Type: ast.NodeBr}
return
} else if bytes.HasPrefix(tags, []byte("<span data-type=")) {
ret = t.processSpanTag(tags, "<span data-type=", "</span>", ctx)
return
} else if bytes.Equal(tags, []byte("<kbd>")) {
} else if bytes.EqualFold(tags, []byte("<kbd>")) {
ret = t.processSpanTag(tags, "<kbd>", "</kbd>", ctx)
return
} else if bytes.Equal(tags, []byte("<u>")) {
} else if bytes.EqualFold(tags, []byte("<u>")) {
ret = t.processSpanTag(tags, "<u>", "</u>", ctx)
return
} else if bytes.Equal(tags, []byte("<sup>")) {
} else if bytes.EqualFold(tags, []byte("<sup>")) {
ret = t.processSpanTag(tags, "<sup>", "</sup>", ctx)
return
} else if bytes.Equal(tags, []byte("<sub>")) {
} else if bytes.EqualFold(tags, []byte("<sub>")) {
ret = t.processSpanTag(tags, "<sub>", "</sub>", ctx)
return
} else if bytes.EqualFold(tags, []byte("<mark>")) {
ret = t.processSpanTag(tags, "<mark>", "</mark>", ctx)
return
} else if bytes.EqualFold(tags, []byte("<s>")) {
ret = t.processSpanTag(tags, "<s>", "</s>", ctx)
return
} else if bytes.EqualFold(tags, []byte("<del>")) {
ret = t.processSpanTag(tags, "<del>", "</del>", ctx)
return
} else if bytes.EqualFold(tags, []byte("<strike>")) {
ret = t.processSpanTag(tags, "<strike>", "</strike>", ctx)
return
} else if bytes.EqualFold(tags, []byte("<em>")) {
ret = t.processSpanTag(tags, "<em>", "</em>", ctx)
return
} else if bytes.EqualFold(tags, []byte("<i>")) {
ret = t.processSpanTag(tags, "<i>", "</i>", ctx)
return
} else if bytes.EqualFold(tags, []byte("<strong>")) {
ret = t.processSpanTag(tags, "<strong>", "</strong>", ctx)
return
} else if bytes.EqualFold(tags, []byte("<b>")) {
ret = t.processSpanTag(tags, "<b>", "</b>", ctx)
return
}
}
ret = &ast.Node{Type: ast.NodeInlineHTML, Tokens: tags}
Expand Down Expand Up @@ -176,12 +200,19 @@ func (t *Tree) processSpanTag(tags []byte, startTag, endTag string, ctx *InlineC

var typ string
startTagLen := len(startTag)
if "<kbd>" == startTag || "<u>" == startTag || "<sup>" == startTag || "<sub>" == startTag {
if "<kbd>" == startTag || "<u>" == startTag || "<sup>" == startTag || "<sub>" == startTag || "<mark>" == startTag || "<s>" == startTag || "<del>" == startTag || "<strike>" == startTag || "<em>" == startTag || "<i>" == startTag || "<strong>" == startTag || "<b>" == startTag {
if !t.Context.ParseOption.HTMLTag2TextMark {
ret = &ast.Node{Type: ast.NodeInlineHTML, Tokens: tags}
return
}
typ = node.Data
if "b" == typ {
typ = "strong"
} else if "i" == typ {
typ = "em"
} else if "del" == typ || "strike" == typ {
typ = "s"
}
} else { // <span data-type="a">
typ = string(tags[startTagLen+1:])
typ = typ[:strings.Index(typ, "\"")]
Expand Down Expand Up @@ -612,7 +643,18 @@ func SetTextMarkNode(node *ast.Node, n *html.Node, options *Options) {
if n.DataAtom == atom.Span {
dataType = "text"
} else {
dataType = n.DataAtom.String()
if "" != node.TextMarkType {
dataType = node.TextMarkType
} else {
dataType = n.DataAtom.String()
if "b" == dataType {
dataType = "strong"
} else if "i" == dataType {
dataType = "em"
} else if "del" == dataType || "strike" == dataType {
dataType = "s"
}
}
}
}
node.TextMarkType = dataType
Expand Down
5 changes: 2 additions & 3 deletions parse/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,9 +405,8 @@ type Options struct {
DataImage bool
// TextMark 设置是否打开通用行级节点解析支持。
TextMark bool
// HTMLTag2TextMark 设置是否打开 HTML 某些标签解析为 TextMark 节点支持。目前仅支持 <u>、<kbd>、<sub> 和 <sup> 标签。
// 这个开关主要用于兼容 Markdown 输入 API 上 https://github.com/siyuan-note/siyuan/issues/6039
// 不用于 Protyle 自旋过程 https://github.com/siyuan-note/siyuan/issues/5877
// HTMLTag2TextMark 设置是否打开 HTML 某些标签解析为 TextMark 节点支持。
// 目前仅支持 <u>、<kbd>、<sub>、<sup>、<strong>/<b>、<em>/<i>、<s>/<del>/<strike> 和 <mark>。
HTMLTag2TextMark bool
// Spin 设置是否打开自旋解析支持,该选项仅用于 Spin 内部过程,设置时请注意使用场景。
//
Expand Down
21 changes: 21 additions & 0 deletions render/format_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,30 @@ func NewFormatRenderer(tree *parse.Tree, options *Options) *FormatRenderer {
ret.RendererFuncs[ast.NodeTextMark] = ret.renderTextMark
ret.RendererFuncs[ast.NodeAttributeView] = ret.renderAttributeView
ret.RendererFuncs[ast.NodeCustomBlock] = ret.renderCustomBlock
ret.RendererFuncs[ast.NodeHTMLTag] = ret.renderHTMLTag
ret.RendererFuncs[ast.NodeHTMLTagOpen] = ret.renderHTMLTagOpen
ret.RendererFuncs[ast.NodeHTMLTagClose] = ret.renderHTMLTagClose
return ret
}

func (r *FormatRenderer) renderHTMLTag(node *ast.Node, entering bool) ast.WalkStatus {
return ast.WalkContinue
}

func (r *FormatRenderer) renderHTMLTagOpen(node *ast.Node, entering bool) ast.WalkStatus {
if entering {
r.Write(node.Tokens)
}
return ast.WalkContinue
}

func (r *FormatRenderer) renderHTMLTagClose(node *ast.Node, entering bool) ast.WalkStatus {
if entering {
r.Write(node.Tokens)
}
return ast.WalkContinue
}

func (r *FormatRenderer) renderCustomBlock(node *ast.Node, entering bool) ast.WalkStatus {
if entering {
r.Newline()
Expand Down
36 changes: 36 additions & 0 deletions test/h2m_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,46 @@ var html2MdTests = []parseTest{
func TestHTML2Md(t *testing.T) {
luteEngine := lute.New()
luteEngine.SetAutoSpace(true)
luteEngine.SetSup(true)
luteEngine.SetSub(true)
luteEngine.SetMark(true)
luteEngine.SetGFMStrikethrough(true)
luteEngine.SetInlineAsterisk(true)
luteEngine.SetInlineUnderscore(true)
for _, test := range html2MdTests {
md := luteEngine.HTML2Md(test.from)
if test.to != md {
t.Fatalf("test case [%s] failed\nexpected\n\t%q\ngot\n\t%q\noriginal html\n\t%q", test.name, test.to, md, test.from)
}
}
}

var html2MdDisableSyntaxTests = []parseTest{

{"7", "<strong><em>foo</em></strong> <em><strong>bar</strong></em>", "<strong><em>foo</em></strong> <em><strong>bar</strong></em>\n"},
{"6", "<kbd>foo</kbd>", "<kbd>foo</kbd>\n"},
{"5", "<mark>foo</mark>", "<mark>foo</mark>\n"},
{"4", "<u>foo</u>", "<u>foo</u>\n"},
{"3", "<s>foo</s> <del>bar</del> <strike>baz</strike>", "<s>foo</s> <s>bar</s> <s>baz</s>\n"},
{"2", "foo<strong>bar</strong> <b>baz</b>", "foo<strong>bar</strong> <strong>baz</strong>\n"},
{"1", "foo<em>bar</em> <i>baz</i>", "foo<em>bar</em> <em>baz</em>\n"},
{"0", "foo<sup>bar</sup></span><sub>baz</sub>", "foo<sup>bar</sup><sub>baz</sub>\n"},
}

func TestHTML2MdDisableSyntax(t *testing.T) {
luteEngine := lute.New()
luteEngine.SetAutoSpace(true)
luteEngine.SetSup(false)
luteEngine.SetSub(false)
luteEngine.SetMark(false)
luteEngine.SetGFMStrikethrough(false)
luteEngine.SetInlineAsterisk(false)
luteEngine.SetInlineUnderscore(false)
luteEngine.SetHTMLTag2TextMark(true)
for _, test := range html2MdDisableSyntaxTests {
md := luteEngine.HTML2Md(test.from)
if test.to != md {
t.Fatalf("test case [%s] failed\nexpected\n\t%q\ngot\n\t%q\noriginal html\n\t%q", test.name, test.to, md, test.from)
}
}
}
Loading

0 comments on commit 982dbd9

Please sign in to comment.