diff --git a/kernel/model/export.go b/kernel/model/export.go index 407431d73..f1c094a22 100644 --- a/kernel/model/export.go +++ b/kernel/model/export.go @@ -1932,62 +1932,9 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold bool, luteEngine := NewLute() ret = tree id := tree.Root.ID - var unlinks []*ast.Node // 解析查询嵌入节点 - ast.Walk(ret.Root, func(n *ast.Node, entering bool) ast.WalkStatus { - if !entering || ast.NodeBlockQueryEmbed != n.Type { - return ast.WalkContinue - } - - var defMd string - stmt := n.ChildByType(ast.NodeBlockQueryEmbedScript).TokensStr() - stmt = html.UnescapeString(stmt) - stmt = strings.ReplaceAll(stmt, editor.IALValEscNewLine, "\n") - embedBlocks := searchEmbedBlock(n.ID, stmt, nil, 0, false) - if 1 > len(embedBlocks) { - return ast.WalkContinue - } - - defMdBuf := bytes.Buffer{} - for _, def := range embedBlocks { - defMdBuf.WriteString(renderBlockMarkdownR(def.Block.ID)) - defMdBuf.WriteString("\n\n") - } - defMd = defMdBuf.String() - - buf := &bytes.Buffer{} - lines := strings.Split(defMd, "\n") - for i, line := range lines { - if 0 == blockEmbedMode { // 原始文本 - buf.WriteString(line) - } else { // Blockquote - buf.WriteString("> " + line) - } - if i < len(lines)-1 { - buf.WriteString("\n") - } - } - buf.WriteString("\n\n") - - refTree := parse.Parse("", buf.Bytes(), luteEngine.ParseOptions) - var children []*ast.Node - for c := refTree.Root.FirstChild; nil != c; c = c.Next { - children = append(children, c) - } - for _, c := range children { - if ast.NodeDocument == c.Type { - continue - } - n.InsertBefore(c) - } - unlinks = append(unlinks, n) - return ast.WalkSkipChildren - }) - for _, n := range unlinks { - n.Unlink() - } - unlinks = nil + resolveEmbedR(ret.Root, blockEmbedMode, luteEngine, &[]string{}) // 收集引用转脚注 var refFootnotes []*refAsFootnotes @@ -1998,6 +1945,7 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold bool, collectFootnotesDefs(ret.ID, &refFootnotes, &treeCache, &depth) } + var unlinks []*ast.Node ast.Walk(ret.Root, func(n *ast.Node, entering bool) ast.WalkStatus { if !entering { return ast.WalkContinue @@ -2393,7 +2341,7 @@ func resolveFootnotesDefs(refFootnotes *[]*refAsFootnotes, rootID string, blockR stmt = strings.ReplaceAll(stmt, editor.IALValEscNewLine, "\n") sqlBlocks := sql.SelectBlocksRawStmt(stmt, 1, Conf.Search.Limit) for _, b := range sqlBlocks { - subNodes := renderBlockMarkdownR0(b.ID, &rendered) + subNodes := renderBlockMarkdownR(b.ID, &rendered) for _, subNode := range subNodes { if ast.NodeListItem == subNode.Type { parentList := &ast.Node{Type: ast.NodeList, ListData: &ast.ListData{Typ: subNode.ListData.Typ}} diff --git a/kernel/model/render.go b/kernel/model/render.go index b6c73080c..a7e6bc6a8 100644 --- a/kernel/model/render.go +++ b/kernel/model/render.go @@ -165,21 +165,83 @@ func renderBlockContentByNodes(nodes []*ast.Node) string { return buf.String() } -func renderBlockMarkdownR(id string) string { - var rendered []string - nodes := renderBlockMarkdownR0(id, &rendered) - buf := bytes.Buffer{} - buf.Grow(4096) - luteEngine := NewLute() - for _, n := range nodes { - md := treenode.FormatNode(n, luteEngine) - buf.WriteString(md) - buf.WriteString("\n\n") +func resolveEmbedR(n *ast.Node, blockEmbedMode int, luteEngine *lute.Lute, resolved *[]string) { + var children []*ast.Node + if ast.NodeHeading == n.Type { + children = append(children, n) + children = append(children, treenode.HeadingChildren(n)...) + } else if ast.NodeDocument == n.Type { + for c := n.FirstChild; nil != c; c = c.Next { + children = append(children, c) + } + } else { + children = append(children, n) } - return buf.String() + + for _, child := range children { + var unlinks []*ast.Node + ast.Walk(child, func(n *ast.Node, entering bool) ast.WalkStatus { + if !entering || !n.IsBlock() { + return ast.WalkContinue + } + + if ast.NodeBlockQueryEmbed == n.Type { + if gulu.Str.Contains(n.ID, *resolved) { + return ast.WalkContinue + } + *resolved = append(*resolved, n.ID) + + stmt := n.ChildByType(ast.NodeBlockQueryEmbedScript).TokensStr() + stmt = html.UnescapeString(stmt) + stmt = strings.ReplaceAll(stmt, editor.IALValEscNewLine, "\n") + sqlBlocks := sql.SelectBlocksRawStmt(stmt, 1, Conf.Search.Limit) + for _, sqlBlock := range sqlBlocks { + md := sqlBlock.Markdown + + if "d" == sqlBlock.Type { + subTree, _ := LoadTreeByBlockID(sqlBlock.ID) + md, _ = lute.FormatNodeSync(subTree.Root, luteEngine.ParseOptions, luteEngine.RenderOptions) + } // 标题块不需要再单独解析,直接使用 Markdown,函数开头处会处理 + + buf := &bytes.Buffer{} + lines := strings.Split(md, "\n") + for i, line := range lines { + if 0 == blockEmbedMode { // 使用原始文本 + buf.WriteString(line) + } else { // 使用引述块 + buf.WriteString("> " + line) + } + if i < len(lines)-1 { + buf.WriteString("\n") + } + } + buf.WriteString("\n\n") + + subTree := parse.Parse("", buf.Bytes(), luteEngine.ParseOptions) + var inserts []*ast.Node + for subNode := subTree.Root.FirstChild; nil != subNode; subNode = subNode.Next { + if ast.NodeKramdownBlockIAL != subNode.Type { + inserts = append(inserts, subNode) + } + } + for _, insert := range inserts { + n.InsertBefore(insert) + resolveEmbedR(insert, blockEmbedMode, luteEngine, resolved) + } + } + unlinks = append(unlinks, n) + return ast.WalkSkipChildren + } + return ast.WalkContinue + }) + for _, unlink := range unlinks { + unlink.Unlink() + } + } + return } -func renderBlockMarkdownR0(id string, rendered *[]string) (ret []*ast.Node) { +func renderBlockMarkdownR(id string, rendered *[]string) (ret []*ast.Node) { if gulu.Str.Contains(id, *rendered) { return } @@ -225,7 +287,7 @@ func renderBlockMarkdownR0(id string, rendered *[]string) (ret []*ast.Node) { stmt = strings.ReplaceAll(stmt, editor.IALValEscNewLine, "\n") sqlBlocks := sql.SelectBlocksRawStmt(stmt, 1, Conf.Search.Limit) for _, sqlBlock := range sqlBlocks { - subNodes := renderBlockMarkdownR0(sqlBlock.ID, rendered) + subNodes := renderBlockMarkdownR(sqlBlock.ID, rendered) for _, subNode := range subNodes { inserts = append(inserts, subNode) }