123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- // Lute - 一款结构化的 Markdown 引擎,支持 Go 和 JavaScript
- // Copyright (c) 2019-present, b3log.org
- //
- // Lute is licensed under Mulan PSL v2.
- // You can use this software according to the terms and conditions of the Mulan PSL v2.
- // You may obtain a copy of Mulan PSL v2 at:
- // http://license.coscl.org.cn/MulanPSL2
- // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- // See the Mulan PSL v2 for more details.
- package filesys
- import (
- "bytes"
- "strings"
- "github.com/88250/lute/ast"
- "github.com/88250/lute/editor"
- "github.com/88250/lute/parse"
- "github.com/88250/lute/util"
- "github.com/goccy/go-json"
- "github.com/siyuan-note/siyuan/kernel/treenode"
- )
- func ParseJSONWithoutFix(jsonData []byte, options *parse.Options) (ret *parse.Tree, err error) {
- root := &ast.Node{}
- err = json.Unmarshal(jsonData, root)
- if nil != err {
- return
- }
- ret = &parse.Tree{Name: "", ID: root.ID, Root: &ast.Node{Type: ast.NodeDocument, ID: root.ID, Spec: root.Spec}, Context: &parse.Context{ParseOption: options}}
- ret.Root.KramdownIAL = parse.Map2IAL(root.Properties)
- ret.Context.Tip = ret.Root
- if nil == root.Children {
- return
- }
- idMap := map[string]bool{}
- for _, child := range root.Children {
- genTreeByJSON(child, ret, &idMap, nil, nil, true)
- }
- return
- }
- func ParseJSON(jsonData []byte, options *parse.Options) (ret *parse.Tree, needFix bool, err error) {
- root := &ast.Node{}
- err = json.Unmarshal(jsonData, root)
- if nil != err {
- return
- }
- ret = &parse.Tree{Name: "", ID: root.ID, Root: &ast.Node{Type: ast.NodeDocument, ID: root.ID, Spec: root.Spec}, Context: &parse.Context{ParseOption: options}}
- ret.Root.KramdownIAL = parse.Map2IAL(root.Properties)
- for _, kv := range ret.Root.KramdownIAL {
- if strings.Contains(kv[1], "\n") {
- val := kv[1]
- val = strings.ReplaceAll(val, "\n", editor.IALValEscNewLine)
- ret.Root.SetIALAttr(kv[0], val)
- needFix = true
- }
- }
- ret.Context.Tip = ret.Root
- if nil == root.Children {
- newPara := &ast.Node{Type: ast.NodeParagraph, ID: ast.NewNodeID()}
- newPara.SetIALAttr("id", newPara.ID)
- ret.Root.AppendChild(newPara)
- needFix = true
- return
- }
- needMigrate2Spec1 := false
- idMap := map[string]bool{}
- for _, child := range root.Children {
- genTreeByJSON(child, ret, &idMap, &needFix, &needMigrate2Spec1, false)
- }
- if nil == ret.Root.FirstChild {
- // 如果是空文档的话挂一个空段落上去
- newP := treenode.NewParagraph()
- ret.Root.AppendChild(newP)
- ret.Root.SetIALAttr("updated", newP.ID[:14])
- }
- if needMigrate2Spec1 {
- parse.NestedInlines2FlattedSpans(ret)
- needFix = true
- }
- return
- }
- func genTreeByJSON(node *ast.Node, tree *parse.Tree, idMap *map[string]bool, needFix, needMigrate2Spec1 *bool, ignoreFix bool) {
- node.Tokens, node.Type = util.StrToBytes(node.Data), ast.Str2NodeType(node.TypeStr)
- node.Data, node.TypeStr = "", ""
- node.KramdownIAL = parse.Map2IAL(node.Properties)
- node.Properties = nil
- if !ignoreFix {
- // 历史数据订正
- if -1 == node.Type {
- *needFix = true
- node.Type = ast.NodeParagraph
- node.AppendChild(&ast.Node{Type: ast.NodeText, Tokens: node.Tokens})
- node.Children = nil
- }
- switch node.Type {
- case ast.NodeList:
- if 1 > len(node.Children) {
- *needFix = true
- return // 忽略空列表
- }
- case ast.NodeListItem:
- if 1 > len(node.Children) {
- *needFix = true
- return // 忽略空列表项
- }
- case ast.NodeBlockquote:
- if 2 > len(node.Children) {
- *needFix = true
- return // 忽略空引述
- }
- case ast.NodeSuperBlock:
- if 4 > len(node.Children) {
- *needFix = true
- return // 忽略空超级块
- }
- case ast.NodeMathBlock:
- if 1 > len(node.Children) {
- *needFix = true
- return // 忽略空公式
- }
- case ast.NodeBlockQueryEmbed:
- if 1 > len(node.Children) {
- *needFix = true
- return // 忽略空查询嵌入块
- }
- }
- fixLegacyData(tree.Context.Tip, node, idMap, needFix, needMigrate2Spec1)
- }
- tree.Context.Tip.AppendChild(node)
- tree.Context.Tip = node
- defer tree.Context.ParentTip()
- if nil == node.Children {
- return
- }
- for _, child := range node.Children {
- genTreeByJSON(child, tree, idMap, needFix, needMigrate2Spec1, ignoreFix)
- }
- node.Children = nil
- }
- func fixLegacyData(tip, node *ast.Node, idMap *map[string]bool, needFix, needMigrate2Spec1 *bool) {
- if node.IsBlock() {
- if "" == node.ID {
- node.ID = ast.NewNodeID()
- node.SetIALAttr("id", node.ID)
- *needFix = true
- }
- if 0 < len(node.Children) && ast.NodeBr.String() == node.Children[len(node.Children)-1].TypeStr {
- // 剔除块尾多余的软换行 https://github.com/siyuan-note/siyuan/issues/6191
- node.Children = node.Children[:len(node.Children)-1]
- *needFix = true
- }
- }
- if "" != node.ID {
- if _, ok := (*idMap)[node.ID]; ok {
- node.ID = ast.NewNodeID()
- node.SetIALAttr("id", node.ID)
- *needFix = true
- }
- (*idMap)[node.ID] = true
- }
- switch node.Type {
- case ast.NodeIFrame:
- if bytes.Contains(node.Tokens, util.StrToBytes("iframe-content")) {
- start := bytes.Index(node.Tokens, util.StrToBytes("<iframe"))
- end := bytes.Index(node.Tokens, util.StrToBytes("</iframe>"))
- node.Tokens = node.Tokens[start : end+9]
- *needFix = true
- }
- case ast.NodeWidget:
- if bytes.Contains(node.Tokens, util.StrToBytes("http://127.0.0.1:6806")) {
- node.Tokens = bytes.ReplaceAll(node.Tokens, []byte("http://127.0.0.1:6806"), nil)
- *needFix = true
- }
- case ast.NodeList:
- if nil != node.ListData && 3 != node.ListData.Typ && 0 < len(node.Children) &&
- nil != node.Children[0].ListData && 3 == node.Children[0].ListData.Typ {
- node.ListData.Typ = 3
- *needFix = true
- }
- case ast.NodeMark:
- if 3 == len(node.Children) && "NodeText" == node.Children[1].TypeStr {
- if strings.HasPrefix(node.Children[1].Data, " ") || strings.HasSuffix(node.Children[1].Data, " ") {
- node.Children[1].Data = strings.TrimSpace(node.Children[1].Data)
- *needFix = true
- }
- }
- case ast.NodeHeading:
- if 6 < node.HeadingLevel {
- node.HeadingLevel = 6
- *needFix = true
- }
- case ast.NodeLinkDest:
- if bytes.HasPrefix(node.Tokens, []byte("assets/")) && bytes.HasSuffix(node.Tokens, []byte(" ")) {
- node.Tokens = bytes.TrimSpace(node.Tokens)
- *needFix = true
- }
- case ast.NodeText:
- if nil != tip.LastChild && ast.NodeTagOpenMarker == tip.LastChild.Type && 1 > len(node.Tokens) {
- node.Tokens = []byte("Untitled")
- *needFix = true
- }
- case ast.NodeTagCloseMarker:
- if nil != tip.LastChild {
- if ast.NodeTagOpenMarker == tip.LastChild.Type {
- tip.AppendChild(&ast.Node{Type: ast.NodeText, Tokens: []byte("Untitled")})
- *needFix = true
- } else if "" == tip.LastChild.Text() {
- tip.LastChild.Type = ast.NodeText
- tip.LastChild.Tokens = []byte("Untitled")
- *needFix = true
- }
- }
- case ast.NodeBlockRef:
- // 建立索引时无法解析 `v2.2.0-` 版本的块引用 https://github.com/siyuan-note/siyuan/issues/6889
- // 早先的迁移程序有缺陷,漏迁移了块引用节点,这里检测到块引用节点后标识需要迁移
- *needMigrate2Spec1 = true
- case ast.NodeInlineHTML:
- *needFix = true
- node.Type = ast.NodeHTMLBlock
- }
- for _, kv := range node.KramdownIAL {
- if strings.Contains(kv[1], "\n") {
- val := kv[1]
- val = strings.ReplaceAll(val, "\n", editor.IALValEscNewLine)
- node.SetIALAttr(kv[0], val)
- *needFix = true
- }
- }
- }
|