json_parser.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // Lute - 一款结构化的 Markdown 引擎,支持 Go 和 JavaScript
  2. // Copyright (c) 2019-present, b3log.org
  3. //
  4. // Lute is licensed under Mulan PSL v2.
  5. // You can use this software according to the terms and conditions of the Mulan PSL v2.
  6. // You may obtain a copy of Mulan PSL v2 at:
  7. // http://license.coscl.org.cn/MulanPSL2
  8. // 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.
  9. // See the Mulan PSL v2 for more details.
  10. package filesys
  11. import (
  12. "bytes"
  13. "strings"
  14. "github.com/88250/lute/ast"
  15. "github.com/88250/lute/editor"
  16. "github.com/88250/lute/parse"
  17. "github.com/88250/lute/util"
  18. "github.com/goccy/go-json"
  19. "github.com/siyuan-note/siyuan/kernel/treenode"
  20. )
  21. func ParseJSONWithoutFix(jsonData []byte, options *parse.Options) (ret *parse.Tree, err error) {
  22. root := &ast.Node{}
  23. err = json.Unmarshal(jsonData, root)
  24. if nil != err {
  25. return
  26. }
  27. ret = &parse.Tree{Name: "", ID: root.ID, Root: &ast.Node{Type: ast.NodeDocument, ID: root.ID, Spec: root.Spec}, Context: &parse.Context{ParseOption: options}}
  28. ret.Root.KramdownIAL = parse.Map2IAL(root.Properties)
  29. ret.Context.Tip = ret.Root
  30. if nil == root.Children {
  31. return
  32. }
  33. idMap := map[string]bool{}
  34. for _, child := range root.Children {
  35. genTreeByJSON(child, ret, &idMap, nil, nil, true)
  36. }
  37. return
  38. }
  39. func ParseJSON(jsonData []byte, options *parse.Options) (ret *parse.Tree, needFix bool, err error) {
  40. root := &ast.Node{}
  41. err = json.Unmarshal(jsonData, root)
  42. if nil != err {
  43. return
  44. }
  45. ret = &parse.Tree{Name: "", ID: root.ID, Root: &ast.Node{Type: ast.NodeDocument, ID: root.ID, Spec: root.Spec}, Context: &parse.Context{ParseOption: options}}
  46. ret.Root.KramdownIAL = parse.Map2IAL(root.Properties)
  47. for _, kv := range ret.Root.KramdownIAL {
  48. if strings.Contains(kv[1], "\n") {
  49. val := kv[1]
  50. val = strings.ReplaceAll(val, "\n", editor.IALValEscNewLine)
  51. ret.Root.SetIALAttr(kv[0], val)
  52. needFix = true
  53. }
  54. }
  55. ret.Context.Tip = ret.Root
  56. if nil == root.Children {
  57. newPara := &ast.Node{Type: ast.NodeParagraph, ID: ast.NewNodeID()}
  58. newPara.SetIALAttr("id", newPara.ID)
  59. ret.Root.AppendChild(newPara)
  60. needFix = true
  61. return
  62. }
  63. needMigrate2Spec1 := false
  64. idMap := map[string]bool{}
  65. for _, child := range root.Children {
  66. genTreeByJSON(child, ret, &idMap, &needFix, &needMigrate2Spec1, false)
  67. }
  68. if nil == ret.Root.FirstChild {
  69. // 如果是空文档的话挂一个空段落上去
  70. newP := treenode.NewParagraph()
  71. ret.Root.AppendChild(newP)
  72. ret.Root.SetIALAttr("updated", newP.ID[:14])
  73. }
  74. if needMigrate2Spec1 {
  75. parse.NestedInlines2FlattedSpans(ret)
  76. needFix = true
  77. }
  78. return
  79. }
  80. func genTreeByJSON(node *ast.Node, tree *parse.Tree, idMap *map[string]bool, needFix, needMigrate2Spec1 *bool, ignoreFix bool) {
  81. node.Tokens, node.Type = util.StrToBytes(node.Data), ast.Str2NodeType(node.TypeStr)
  82. node.Data, node.TypeStr = "", ""
  83. node.KramdownIAL = parse.Map2IAL(node.Properties)
  84. node.Properties = nil
  85. if !ignoreFix {
  86. // 历史数据订正
  87. if -1 == node.Type {
  88. *needFix = true
  89. node.Type = ast.NodeParagraph
  90. node.AppendChild(&ast.Node{Type: ast.NodeText, Tokens: node.Tokens})
  91. node.Children = nil
  92. }
  93. switch node.Type {
  94. case ast.NodeList:
  95. if 1 > len(node.Children) {
  96. *needFix = true
  97. return // 忽略空列表
  98. }
  99. case ast.NodeListItem:
  100. if 1 > len(node.Children) {
  101. *needFix = true
  102. return // 忽略空列表项
  103. }
  104. case ast.NodeBlockquote:
  105. if 2 > len(node.Children) {
  106. *needFix = true
  107. return // 忽略空引述
  108. }
  109. case ast.NodeSuperBlock:
  110. if 4 > len(node.Children) {
  111. *needFix = true
  112. return // 忽略空超级块
  113. }
  114. case ast.NodeMathBlock:
  115. if 1 > len(node.Children) {
  116. *needFix = true
  117. return // 忽略空公式
  118. }
  119. case ast.NodeBlockQueryEmbed:
  120. if 1 > len(node.Children) {
  121. *needFix = true
  122. return // 忽略空查询嵌入块
  123. }
  124. }
  125. fixLegacyData(tree.Context.Tip, node, idMap, needFix, needMigrate2Spec1)
  126. }
  127. tree.Context.Tip.AppendChild(node)
  128. tree.Context.Tip = node
  129. defer tree.Context.ParentTip()
  130. if nil == node.Children {
  131. return
  132. }
  133. for _, child := range node.Children {
  134. genTreeByJSON(child, tree, idMap, needFix, needMigrate2Spec1, ignoreFix)
  135. }
  136. node.Children = nil
  137. }
  138. func fixLegacyData(tip, node *ast.Node, idMap *map[string]bool, needFix, needMigrate2Spec1 *bool) {
  139. if node.IsBlock() {
  140. if "" == node.ID {
  141. node.ID = ast.NewNodeID()
  142. node.SetIALAttr("id", node.ID)
  143. *needFix = true
  144. }
  145. if 0 < len(node.Children) && ast.NodeBr.String() == node.Children[len(node.Children)-1].TypeStr {
  146. // 剔除块尾多余的软换行 https://github.com/siyuan-note/siyuan/issues/6191
  147. node.Children = node.Children[:len(node.Children)-1]
  148. *needFix = true
  149. }
  150. }
  151. if "" != node.ID {
  152. if _, ok := (*idMap)[node.ID]; ok {
  153. node.ID = ast.NewNodeID()
  154. node.SetIALAttr("id", node.ID)
  155. *needFix = true
  156. }
  157. (*idMap)[node.ID] = true
  158. }
  159. switch node.Type {
  160. case ast.NodeIFrame:
  161. if bytes.Contains(node.Tokens, util.StrToBytes("iframe-content")) {
  162. start := bytes.Index(node.Tokens, util.StrToBytes("<iframe"))
  163. end := bytes.Index(node.Tokens, util.StrToBytes("</iframe>"))
  164. node.Tokens = node.Tokens[start : end+9]
  165. *needFix = true
  166. }
  167. case ast.NodeWidget:
  168. if bytes.Contains(node.Tokens, util.StrToBytes("http://127.0.0.1:6806")) {
  169. node.Tokens = bytes.ReplaceAll(node.Tokens, []byte("http://127.0.0.1:6806"), nil)
  170. *needFix = true
  171. }
  172. case ast.NodeList:
  173. if nil != node.ListData && 3 != node.ListData.Typ && 0 < len(node.Children) &&
  174. nil != node.Children[0].ListData && 3 == node.Children[0].ListData.Typ {
  175. node.ListData.Typ = 3
  176. *needFix = true
  177. }
  178. case ast.NodeMark:
  179. if 3 == len(node.Children) && "NodeText" == node.Children[1].TypeStr {
  180. if strings.HasPrefix(node.Children[1].Data, " ") || strings.HasSuffix(node.Children[1].Data, " ") {
  181. node.Children[1].Data = strings.TrimSpace(node.Children[1].Data)
  182. *needFix = true
  183. }
  184. }
  185. case ast.NodeHeading:
  186. if 6 < node.HeadingLevel {
  187. node.HeadingLevel = 6
  188. *needFix = true
  189. }
  190. case ast.NodeLinkDest:
  191. if bytes.HasPrefix(node.Tokens, []byte("assets/")) && bytes.HasSuffix(node.Tokens, []byte(" ")) {
  192. node.Tokens = bytes.TrimSpace(node.Tokens)
  193. *needFix = true
  194. }
  195. case ast.NodeText:
  196. if nil != tip.LastChild && ast.NodeTagOpenMarker == tip.LastChild.Type && 1 > len(node.Tokens) {
  197. node.Tokens = []byte("Untitled")
  198. *needFix = true
  199. }
  200. case ast.NodeTagCloseMarker:
  201. if nil != tip.LastChild {
  202. if ast.NodeTagOpenMarker == tip.LastChild.Type {
  203. tip.AppendChild(&ast.Node{Type: ast.NodeText, Tokens: []byte("Untitled")})
  204. *needFix = true
  205. } else if "" == tip.LastChild.Text() {
  206. tip.LastChild.Type = ast.NodeText
  207. tip.LastChild.Tokens = []byte("Untitled")
  208. *needFix = true
  209. }
  210. }
  211. case ast.NodeBlockRef:
  212. // 建立索引时无法解析 `v2.2.0-` 版本的块引用 https://github.com/siyuan-note/siyuan/issues/6889
  213. // 早先的迁移程序有缺陷,漏迁移了块引用节点,这里检测到块引用节点后标识需要迁移
  214. *needMigrate2Spec1 = true
  215. case ast.NodeInlineHTML:
  216. *needFix = true
  217. node.Type = ast.NodeHTMLBlock
  218. }
  219. for _, kv := range node.KramdownIAL {
  220. if strings.Contains(kv[1], "\n") {
  221. val := kv[1]
  222. val = strings.ReplaceAll(val, "\n", editor.IALValEscNewLine)
  223. node.SetIALAttr(kv[0], val)
  224. *needFix = true
  225. }
  226. }
  227. }