Forráskód Böngészése

:zap: 改进多个功能点的性能 Fix https://github.com/siyuan-note/siyuan/issues/7177

Liang Ding 2 éve
szülő
commit
04f47749f7

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
app/stage/protyle/js/lute/lute.min.js


+ 2 - 2
kernel/api/block_op.go

@@ -17,12 +17,12 @@
 package api
 
 import (
+	"github.com/siyuan-note/siyuan/kernel/treenode"
 	"net/http"
 
 	"github.com/88250/gulu"
 	"github.com/88250/lute"
 	"github.com/88250/lute/ast"
-	"github.com/88250/lute/parse"
 	"github.com/gin-gonic/gin"
 	"github.com/siyuan-note/siyuan/kernel/model"
 	"github.com/siyuan-note/siyuan/kernel/util"
@@ -299,7 +299,7 @@ func dataBlockDOM(data string, luteEngine *lute.Lute) (ret string) {
 	ret = luteEngine.Md2BlockDOM(data, true)
 	if "" == ret {
 		// 使用 API 插入空字符串出现错误 https://github.com/siyuan-note/siyuan/issues/3931
-		blankParagraph := parse.NewParagraph()
+		blankParagraph := treenode.NewParagraph()
 		ret = lute.RenderNodeBlockDOM(blankParagraph, luteEngine.ParseOptions, luteEngine.RenderOptions)
 	}
 	return

+ 2 - 1
kernel/api/lute.go

@@ -17,6 +17,7 @@
 package api
 
 import (
+	"github.com/siyuan-note/siyuan/kernel/treenode"
 	"net/http"
 	"path/filepath"
 	"strings"
@@ -70,7 +71,7 @@ func html2BlockDOM(c *gin.Context) {
 		}
 
 		if ast.NodeListItem == n.Type && nil == n.FirstChild {
-			newNode := parse.NewParagraph()
+			newNode := treenode.NewParagraph()
 			n.AppendChild(newNode)
 			n.SetIALAttr("updated", util.TimeFromID(newNode.ID))
 			return ast.WalkSkipChildren

+ 245 - 0
kernel/filesys/json_parser.go

@@ -0,0 +1,245 @@
+// 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
+	}
+
+	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
+		}
+	}
+}

+ 2 - 2
kernel/filesys/tree.go

@@ -151,7 +151,7 @@ func prepareWriteTree(tree *parse.Tree) (data []byte, filePath string, err error
 	luteEngine := util.NewLute() // 不关注用户的自定义解析渲染选项
 
 	if nil == tree.Root.FirstChild {
-		newP := parse.NewParagraph()
+		newP := treenode.NewParagraph()
 		tree.Root.AppendChild(newP)
 		tree.Root.SetIALAttr("updated", util.TimeFromID(newP.ID))
 		treenode.IndexBlockTree(tree)
@@ -229,7 +229,7 @@ func recoverParseJSON2Tree(boxID, p, filePath string, luteEngine *lute.Lute) (re
 func parseJSON2Tree(boxID, p string, jsonData []byte, luteEngine *lute.Lute) (ret *parse.Tree) {
 	var err error
 	var needFix bool
-	ret, needFix, err = parse.ParseJSON(jsonData, luteEngine.ParseOptions)
+	ret, needFix, err = ParseJSON(jsonData, luteEngine.ParseOptions)
 	if nil != err {
 		logging.LogErrorf("parse json [%s] to tree failed: %s", boxID+p, err)
 		return

+ 2 - 2
kernel/go.mod

@@ -6,7 +6,7 @@ require (
 	github.com/88250/clipboard v0.1.5
 	github.com/88250/css v0.1.2
 	github.com/88250/gulu v1.2.3-0.20221117052724-cd06804db798
-	github.com/88250/lute v1.7.5
+	github.com/88250/lute v1.7.6-0.20230127125719-f7a1c703548a
 	github.com/88250/pdfcpu v0.3.13
 	github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1
 	github.com/ConradIrwin/font v0.0.0-20210318200717-ce8d41cc0732
@@ -27,6 +27,7 @@ require (
 	github.com/gin-contrib/gzip v0.0.6
 	github.com/gin-contrib/sessions v0.0.5
 	github.com/gin-gonic/gin v1.8.2
+	github.com/goccy/go-json v0.10.0
 	github.com/gofrs/flock v0.8.1
 	github.com/imroc/req/v3 v3.30.0
 	github.com/jinzhu/copier v0.3.5
@@ -73,7 +74,6 @@ require (
 	github.com/go-playground/universal-translator v0.18.0 // indirect
 	github.com/go-playground/validator/v10 v10.11.1 // indirect
 	github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
-	github.com/goccy/go-json v0.10.0 // indirect
 	github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
 	github.com/golang/glog v1.0.0 // indirect
 	github.com/google/uuid v1.3.0 // indirect

+ 2 - 2
kernel/go.sum

@@ -17,8 +17,8 @@ github.com/88250/go-sqlite3 v1.14.13-0.20220714142610-fbbda1ee84f5 h1:8HdZozCsXS
 github.com/88250/go-sqlite3 v1.14.13-0.20220714142610-fbbda1ee84f5/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 github.com/88250/gulu v1.2.3-0.20221117052724-cd06804db798 h1:sR/s/Y9wyl79ZRCUERwLPo9zqaB3KhNRodCMTJ4ozEU=
 github.com/88250/gulu v1.2.3-0.20221117052724-cd06804db798/go.mod h1:I1qBzsksFL2ciGSuqDE7R3XW4BUMrfDgOvSXEk7FsAI=
-github.com/88250/lute v1.7.5 h1:mcPFURh5sK1WH1kFRjqK5DkMWOfVN2BhyrXitN8GmpQ=
-github.com/88250/lute v1.7.5/go.mod h1:cEoBGi0zArPqAsp0MdG9SKinvH/xxZZWXU7sRx8vHSA=
+github.com/88250/lute v1.7.6-0.20230127125719-f7a1c703548a h1:zqWeg1AOmN2X9tD0AD0V5BE40eQOOxuFqB0LSGiw6tQ=
+github.com/88250/lute v1.7.6-0.20230127125719-f7a1c703548a/go.mod h1:cEoBGi0zArPqAsp0MdG9SKinvH/xxZZWXU7sRx8vHSA=
 github.com/88250/pdfcpu v0.3.13 h1:touMWMZkCGalMIbEg9bxYp7rETM+zwb9hXjwhqi4I7Q=
 github.com/88250/pdfcpu v0.3.13/go.mod h1:S5YT38L/GCjVjmB4PB84PymA1qfopjEhfhTNQilLpv4=
 github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1 h1:48T899JQDwyyRu9yXHePYlPdHtpJfrJEUGBMH3SMBWY=

+ 1 - 1
kernel/model/block.go

@@ -138,7 +138,7 @@ func SwapBlockRef(refID, defID string, includeChildren bool) (err error) {
 		}
 	}
 
-	refPivot := parse.NewParagraph()
+	refPivot := treenode.NewParagraph()
 	refNode.InsertBefore(refPivot)
 
 	if ast.NodeListItem == defNode.Type {

+ 1 - 1
kernel/model/box.go

@@ -425,7 +425,7 @@ func parseKTree(kramdown []byte) (ret *parse.Tree) {
 
 func genTreeID(tree *parse.Tree) {
 	if nil == tree.Root.FirstChild {
-		tree.Root.AppendChild(parse.NewParagraph())
+		tree.Root.AppendChild(treenode.NewParagraph())
 	}
 
 	ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {

+ 6 - 1
kernel/model/file.go

@@ -447,6 +447,11 @@ func getMarkSpanEnd() string {
 }
 
 func GetDoc(startID, endID, id string, index int, keyword string, mode int, size int, isBacklink bool) (blockCount, childBlockCount int, dom, parentID, parent2ID, rootID, typ string, eof bool, boxID, docPath string, isBacklinkExpand bool, err error) {
+	//os.MkdirAll("pprof", 0755)
+	//cpuProfile, _ := os.Create("pprof/GetDoc")
+	//pprof.StartCPUProfile(cpuProfile)
+	//defer pprof.StopCPUProfile()
+
 	WaitForWritingFiles() // 写入数据时阻塞,避免获取到的数据不一致
 
 	inputIndex := index
@@ -1451,7 +1456,7 @@ func createDoc(boxID, p, title, dom string) (tree *parse.Tree, err error) {
 	updated := util.TimeFromID(id)
 	tree.Root.KramdownIAL = [][]string{{"id", id}, {"title", html.EscapeAttrVal(title)}, {"updated", updated}}
 	if nil == tree.Root.FirstChild {
-		tree.Root.AppendChild(parse.NewParagraph())
+		tree.Root.AppendChild(treenode.NewParagraph())
 	}
 
 	transaction := &Transaction{DoOperations: []*Operation{{Action: "create", Data: tree}}}

+ 2 - 2
kernel/model/heading.go

@@ -166,7 +166,7 @@ func Doc2Heading(srcID, targetID string, after bool) (srcTreeBox, srcTreePath st
 	if "" != tagIAL && 0 < len(tags) {
 		// 带标签的文档块转换为标题块时将标签移动到标题块下方 https://github.com/siyuan-note/siyuan/issues/6550
 
-		tagPara := parse.NewParagraph()
+		tagPara := treenode.NewParagraph()
 		for i, tag := range tags {
 			if "" == tag {
 				continue
@@ -319,7 +319,7 @@ func Heading2Doc(srcHeadingID, targetBoxID, targetPath string) (srcRootBlockID,
 	headingNode.Unlink()
 	srcTree.Root.SetIALAttr("updated", util.CurrentTimeSecondsStr())
 	if nil == srcTree.Root.FirstChild {
-		srcTree.Root.AppendChild(parse.NewParagraph())
+		srcTree.Root.AppendChild(treenode.NewParagraph())
 	}
 	if err = indexWriteJSONQueue(srcTree); nil != err {
 		return "", "", err

+ 3 - 2
kernel/model/history.go

@@ -19,7 +19,6 @@ package model
 import (
 	"encoding/json"
 	"fmt"
-	"github.com/siyuan-note/siyuan/kernel/task"
 	"io/fs"
 	"math"
 	"os"
@@ -37,8 +36,10 @@ import (
 	"github.com/siyuan-note/filelock"
 	"github.com/siyuan-note/logging"
 	"github.com/siyuan-note/siyuan/kernel/conf"
+	"github.com/siyuan-note/siyuan/kernel/filesys"
 	"github.com/siyuan-note/siyuan/kernel/search"
 	"github.com/siyuan-note/siyuan/kernel/sql"
+	"github.com/siyuan-note/siyuan/kernel/task"
 	"github.com/siyuan-note/siyuan/kernel/treenode"
 	"github.com/siyuan-note/siyuan/kernel/util"
 )
@@ -154,7 +155,7 @@ func GetDocHistoryContent(historyPath, keyword string) (id, rootID, content stri
 	isLargeDoc = 1024*1024*1 <= len(data)
 
 	luteEngine := NewLute()
-	historyTree, err := parse.ParseJSONWithoutFix(data, luteEngine.ParseOptions)
+	historyTree, err := filesys.ParseJSONWithoutFix(data, luteEngine.ParseOptions)
 	if nil != err {
 		logging.LogErrorf("parse tree from file [%s] failed, remove it", historyPath)
 		os.RemoveAll(historyPath)

+ 2 - 1
kernel/model/import.go

@@ -45,6 +45,7 @@ import (
 	"github.com/88250/lute/render"
 	"github.com/siyuan-note/filelock"
 	"github.com/siyuan-note/logging"
+	"github.com/siyuan-note/siyuan/kernel/filesys"
 	"github.com/siyuan-note/siyuan/kernel/sql"
 	"github.com/siyuan-note/siyuan/kernel/treenode"
 	"github.com/siyuan-note/siyuan/kernel/util"
@@ -125,7 +126,7 @@ func ImportSY(zipPath, boxID, toPath string) (err error) {
 			err = readErr
 			return
 		}
-		tree, _, parseErr := parse.ParseJSON(data, luteEngine.ParseOptions)
+		tree, _, parseErr := filesys.ParseJSON(data, luteEngine.ParseOptions)
 		if nil != parseErr {
 			logging.LogErrorf("parse .sy [%s] failed: %s", syPath, parseErr)
 			err = parseErr

+ 2 - 2
kernel/model/listitem.go

@@ -72,7 +72,7 @@ func ListItem2Doc(srcListItemID, targetBoxID, targetPath string) (srcRootBlockID
 		children = append(children, c)
 	}
 	if 1 > len(children) {
-		newNode := parse.NewParagraph()
+		newNode := treenode.NewParagraph()
 		children = append(children, newNode)
 	}
 
@@ -96,7 +96,7 @@ func ListItem2Doc(srcListItemID, targetBoxID, targetPath string) (srcRootBlockID
 	}
 	srcTree.Root.SetIALAttr("updated", util.CurrentTimeSecondsStr())
 	if nil == srcTree.Root.FirstChild {
-		srcTree.Root.AppendChild(parse.NewParagraph())
+		srcTree.Root.AppendChild(treenode.NewParagraph())
 	}
 	if err = indexWriteJSONQueue(srcTree); nil != err {
 		return "", "", err

+ 4 - 3
kernel/model/repository.go

@@ -23,7 +23,6 @@ import (
 	"encoding/base64"
 	"errors"
 	"fmt"
-	"github.com/siyuan-note/siyuan/kernel/task"
 	"math"
 	"net/http"
 	"os"
@@ -49,7 +48,9 @@ import (
 	"github.com/siyuan-note/logging"
 	"github.com/siyuan-note/siyuan/kernel/cache"
 	"github.com/siyuan-note/siyuan/kernel/conf"
+	"github.com/siyuan-note/siyuan/kernel/filesys"
 	"github.com/siyuan-note/siyuan/kernel/sql"
+	"github.com/siyuan-note/siyuan/kernel/task"
 	"github.com/siyuan-note/siyuan/kernel/treenode"
 	"github.com/siyuan-note/siyuan/kernel/util"
 	"github.com/studio-b12/gowebdav"
@@ -255,7 +256,7 @@ func parseTitleInSnapshot(fileID string, repo *dejavu.Repo, luteEngine *lute.Lut
 	}
 
 	var tree *parse.Tree
-	tree, err = parse.ParseJSONWithoutFix(data, luteEngine.ParseOptions)
+	tree, err = filesys.ParseJSONWithoutFix(data, luteEngine.ParseOptions)
 	if nil != err {
 		logging.LogErrorf("parse file [%s] failed: %s", fileID, err)
 		return
@@ -277,7 +278,7 @@ func parseTreeInSnapshot(fileID string, repo *dejavu.Repo, luteEngine *lute.Lute
 	}
 
 	isLargeDoc = 1024*1024*1 <= len(data)
-	tree, err = parse.ParseJSONWithoutFix(data, luteEngine.ParseOptions)
+	tree, err = filesys.ParseJSONWithoutFix(data, luteEngine.ParseOptions)
 	if nil != err {
 		return
 	}

+ 8 - 8
kernel/model/template.go

@@ -20,13 +20,6 @@ import (
 	"bytes"
 	"errors"
 	"fmt"
-	"github.com/88250/lute/ast"
-	"github.com/88250/lute/parse"
-	"github.com/88250/lute/render"
-	"github.com/araddon/dateparse"
-	"github.com/siyuan-note/logging"
-	"github.com/siyuan-note/siyuan/kernel/treenode"
-	"github.com/siyuan-note/siyuan/kernel/util"
 	"io/fs"
 	"os"
 	"path/filepath"
@@ -36,9 +29,16 @@ import (
 	"time"
 
 	"github.com/88250/gulu"
+	"github.com/88250/lute/ast"
+	"github.com/88250/lute/parse"
+	"github.com/88250/lute/render"
 	sprig "github.com/Masterminds/sprig/v3"
+	"github.com/araddon/dateparse"
+	"github.com/siyuan-note/logging"
 	"github.com/siyuan-note/siyuan/kernel/search"
 	"github.com/siyuan-note/siyuan/kernel/sql"
+	"github.com/siyuan-note/siyuan/kernel/treenode"
+	"github.com/siyuan-note/siyuan/kernel/util"
 )
 
 func RenderGoTemplate(templateContent string) (ret string, err error) {
@@ -258,7 +258,7 @@ func renderTemplate(p, id string) (string, error) {
 		return ast.WalkContinue
 	})
 	for _, n := range nodesNeedAppendChild {
-		n.AppendChild(parse.NewParagraph())
+		n.AppendChild(treenode.NewParagraph())
 	}
 	for _, n := range unlinks {
 		n.Unlink()

+ 1 - 1
kernel/model/tree.go

@@ -131,7 +131,7 @@ func loadTree(localPath string, luteEngine *lute.Lute) (ret *parse.Tree, err err
 		return
 	}
 
-	ret, err = parse.ParseJSONWithoutFix(data, luteEngine.ParseOptions)
+	ret, err = filesys.ParseJSONWithoutFix(data, luteEngine.ParseOptions)
 	if nil != err {
 		logging.LogErrorf("parse json to tree [%s] failed: %s", localPath, err)
 		return

+ 1 - 2
kernel/model/widget.go

@@ -22,9 +22,8 @@ import (
 	"strings"
 
 	"github.com/siyuan-note/logging"
-	"github.com/siyuan-note/siyuan/kernel/util"
-
 	"github.com/siyuan-note/siyuan/kernel/search"
+	"github.com/siyuan-note/siyuan/kernel/util"
 )
 
 func SearchWidget(keyword string) (ret []*Block) {

+ 8 - 0
kernel/treenode/tree.go

@@ -114,3 +114,11 @@ func RootChildIDs(rootID string) (ret []string) {
 	})
 	return
 }
+
+func NewParagraph() (ret *ast.Node) {
+	newID := ast.NewNodeID()
+	ret = &ast.Node{ID: newID, Type: ast.NodeParagraph}
+	ret.SetIALAttr("id", newID)
+	ret.SetIALAttr("updated", newID[:14])
+	return
+}

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott