🧑‍💻 Improve kernel API /api/block/updateBlock and /api/block/batchUpdateBlock https://github.com/siyuan-note/siyuan/issues/15301

This commit is contained in:
Daniel 2025-07-15 23:39:59 +08:00
parent 049b4d4b4b
commit 70e31c71cc
No known key found for this signature in database
GPG key ID: 86211BA83DF03017
9 changed files with 59 additions and 45 deletions

View file

@ -575,7 +575,6 @@ func updateBlock(c *gin.Context) {
return return
} }
var transactions []*model.Transaction
if "NodeDocument" == block.Type { if "NodeDocument" == block.Type {
oldTree, err := filesys.LoadTree(block.Box, block.Path, luteEngine) oldTree, err := filesys.LoadTree(block.Box, block.Path, luteEngine)
if err != nil { if err != nil {
@ -584,39 +583,46 @@ func updateBlock(c *gin.Context) {
return return
} }
var toRemoves []*ast.Node var toRemoves []*ast.Node
var ops []*model.Operation
for n := oldTree.Root.FirstChild; nil != n; n = n.Next { for n := oldTree.Root.FirstChild; nil != n; n = n.Next {
toRemoves = append(toRemoves, n) toRemoves = append(toRemoves, n)
ops = append(ops, &model.Operation{Action: "delete", ID: n.ID})
} }
for _, n := range toRemoves { for _, n := range toRemoves {
n.Unlink() n.Unlink()
} }
ops = append(ops, &model.Operation{Action: "appendInsert", Data: data, ParentID: id})
transactions = append(transactions, &model.Transaction{
DoOperations: ops,
})
} else {
if "NodeListItem" == block.Type && ast.NodeList == tree.Root.FirstChild.Type {
// 使用 API `api/block/updateBlock` 更新列表项时渲染错误 https://github.com/siyuan-note/siyuan/issues/4658
tree.Root.AppendChild(tree.Root.FirstChild.FirstChild) // 将列表下的第一个列表项移到文档结尾,移动以后根下面直接挂列表项,渲染器可以正常工作
tree.Root.FirstChild.Unlink() // 删除列表
tree.Root.FirstChild.Unlink() // 继续删除列表 IAL
}
tree.Root.FirstChild.SetIALAttr("id", id)
data = luteEngine.Tree2BlockDOM(tree, luteEngine.RenderOptions) var toAppends []*ast.Node
transactions = []*model.Transaction{ for n := tree.Root.FirstChild; nil != n; n = n.Next {
{ toAppends = append(toAppends, n)
DoOperations: []*model.Operation{ }
{ for _, n := range toAppends {
Action: "update", oldTree.Root.AppendChild(n)
ID: id, }
Data: data,
}, model.WriteTreeUpsertQueue(oldTree)
model.ReloadProtyle(id)
return
}
var transactions []*model.Transaction
if "NodeListItem" == block.Type && ast.NodeList == tree.Root.FirstChild.Type {
// 使用 API `api/block/updateBlock` 更新列表项时渲染错误 https://github.com/siyuan-note/siyuan/issues/4658
tree.Root.AppendChild(tree.Root.FirstChild.FirstChild) // 将列表下的第一个列表项移到文档结尾,移动以后根下面直接挂列表项,渲染器可以正常工作
tree.Root.FirstChild.Unlink() // 删除列表
tree.Root.FirstChild.Unlink() // 继续删除列表 IAL
}
tree.Root.FirstChild.SetIALAttr("id", id)
data = luteEngine.Tree2BlockDOM(tree, luteEngine.RenderOptions)
transactions = []*model.Transaction{
{
DoOperations: []*model.Operation{
{
Action: "update",
ID: id,
Data: data,
}, },
}, },
} },
} }
model.PerformTransactions(&transactions) model.PerformTransactions(&transactions)
@ -704,15 +710,22 @@ func batchUpdateBlock(c *gin.Context) {
return return
} }
var toRemoves []*ast.Node var toRemoves []*ast.Node
for n := oldTree.Root.FirstChild; nil != n; n = n.Next { for n := oldTree.Root.FirstChild; nil != n; n = n.Next {
toRemoves = append(toRemoves, n) toRemoves = append(toRemoves, n)
ops = append(ops, &model.Operation{Action: "delete", ID: n.ID})
} }
for _, n := range toRemoves { for _, n := range toRemoves {
n.Unlink() n.Unlink()
} }
ops = append(ops, &model.Operation{Action: "appendInsert", Data: data, ParentID: id}) var toAppends []*ast.Node
for n := tree.Root.FirstChild; nil != n; n = n.Next {
toAppends = append(toAppends, n)
}
for _, n := range toAppends {
oldTree.Root.AppendChild(n)
}
model.WriteTreeUpsertQueue(oldTree)
model.ReloadProtyle(id)
} else { } else {
if "NodeListItem" == block.Type && ast.NodeList == tree.Root.FirstChild.Type { if "NodeListItem" == block.Type && ast.NodeList == tree.Root.FirstChild.Type {
// 使用 API `api/block/updateBlock` 更新列表项时渲染错误 https://github.com/siyuan-note/siyuan/issues/4658 // 使用 API `api/block/updateBlock` 更新列表项时渲染错误 https://github.com/siyuan-note/siyuan/issues/4658
@ -731,13 +744,14 @@ func batchUpdateBlock(c *gin.Context) {
} }
} }
tx.DoOperations = ops if 0 < len(ops) {
model.PerformTransactions(&transactions) tx.DoOperations = ops
model.FlushTxQueue() model.PerformTransactions(&transactions)
model.FlushTxQueue()
ret.Data = transactions
broadcastTransactions(transactions)
ret.Data = transactions
broadcastTransactions(transactions)
}
} }
func deleteBlock(c *gin.Context) { func deleteBlock(c *gin.Context) {

View file

@ -321,7 +321,7 @@ func NetAssets2LocalAssets(rootID string, onlyImg bool, originalURL string) (err
util.PushClearMsg(msgId) util.PushClearMsg(msgId)
if 0 < files { if 0 < files {
msgId = util.PushMsg(Conf.Language(113), 7000) msgId = util.PushMsg(Conf.Language(113), 7000)
if err = writeTreeUpsertQueue(tree); err != nil { if err = WriteTreeUpsertQueue(tree); err != nil {
return return
} }
util.PushUpdateMsg(msgId, fmt.Sprintf(Conf.Language(120), files), 5000) util.PushUpdateMsg(msgId, fmt.Sprintf(Conf.Language(120), files), 5000)

View file

@ -66,7 +66,7 @@ func RemoveBookmark(bookmark string) (err error) {
} }
util.PushEndlessProgress(fmt.Sprintf(Conf.Language(111), util.EscapeHTML(tree.Root.IALAttr("title")))) util.PushEndlessProgress(fmt.Sprintf(Conf.Language(111), util.EscapeHTML(tree.Root.IALAttr("title"))))
if err = writeTreeUpsertQueue(tree); err != nil { if err = WriteTreeUpsertQueue(tree); err != nil {
util.ClearPushProgress(100) util.ClearPushProgress(100)
return return
} }
@ -124,7 +124,7 @@ func RenameBookmark(oldBookmark, newBookmark string) (err error) {
} }
util.PushEndlessProgress(fmt.Sprintf(Conf.Language(111), util.EscapeHTML(tree.Root.IALAttr("title")))) util.PushEndlessProgress(fmt.Sprintf(Conf.Language(111), util.EscapeHTML(tree.Root.IALAttr("title"))))
if err = writeTreeUpsertQueue(tree); err != nil { if err = WriteTreeUpsertQueue(tree); err != nil {
util.ClearPushProgress(100) util.ClearPushProgress(100)
return return
} }

View file

@ -310,7 +310,7 @@ func Export2Liandi(id string) (err error) {
articleId = result.Data.(string) articleId = result.Data.(string)
tree, _ = LoadTreeByBlockID(id) // 这里必须重新加载,因为前面导出时已经修改了树结构 tree, _ = LoadTreeByBlockID(id) // 这里必须重新加载,因为前面导出时已经修改了树结构
tree.Root.SetIALAttr(liandiArticleIdAttrName, articleId) tree.Root.SetIALAttr(liandiArticleIdAttrName, articleId)
if err = writeTreeUpsertQueue(tree); err != nil { if err = WriteTreeUpsertQueue(tree); err != nil {
return return
} }
} }

View file

@ -922,7 +922,7 @@ func loadNodesByMode(node *ast.Node, inputIndex, mode, size int, isDoc, isHeadin
return return
} }
func writeTreeUpsertQueue(tree *parse.Tree) (err error) { func WriteTreeUpsertQueue(tree *parse.Tree) (err error) {
size, err := filesys.WriteTree(tree) size, err := filesys.WriteTree(tree)
if err != nil { if err != nil {
return return
@ -944,7 +944,7 @@ func indexWriteTreeIndexQueue(tree *parse.Tree) (err error) {
func indexWriteTreeUpsertQueue(tree *parse.Tree) (err error) { func indexWriteTreeUpsertQueue(tree *parse.Tree) (err error) {
treenode.UpsertBlockTree(tree) treenode.UpsertBlockTree(tree)
return writeTreeUpsertQueue(tree) return WriteTreeUpsertQueue(tree)
} }
func renameWriteJSONQueue(tree *parse.Tree) (err error) { func renameWriteJSONQueue(tree *parse.Tree) (err error) {

View file

@ -68,7 +68,7 @@ func AutoSpace(rootID string) (err error) {
newTree.Path = tree.Path newTree.Path = tree.Path
newTree.HPath = tree.HPath newTree.HPath = tree.HPath
newTree.Box = tree.Box newTree.Box = tree.Box
err = writeTreeUpsertQueue(newTree) err = WriteTreeUpsertQueue(newTree)
if err != nil { if err != nil {
return return
} }

View file

@ -932,7 +932,7 @@ func FindReplace(keyword, replacement string, replaceTypes map[string]bool, ids
} }
} }
if err = writeTreeUpsertQueue(tree); err != nil { if err = WriteTreeUpsertQueue(tree); err != nil {
return return
} }
updateNodes[id] = node updateNodes[id] = node

View file

@ -96,7 +96,7 @@ func RemoveTag(label string) (err error) {
n.Unlink() n.Unlink()
} }
util.PushEndlessProgress(fmt.Sprintf(Conf.Language(111), util.EscapeHTML(tree.Root.IALAttr("title")))) util.PushEndlessProgress(fmt.Sprintf(Conf.Language(111), util.EscapeHTML(tree.Root.IALAttr("title"))))
if err = writeTreeUpsertQueue(tree); err != nil { if err = WriteTreeUpsertQueue(tree); err != nil {
util.ClearPushProgress(100) util.ClearPushProgress(100)
return return
} }
@ -195,7 +195,7 @@ func RenameTag(oldLabel, newLabel string) (err error) {
updateNodes[node.ID] = node updateNodes[node.ID] = node
} }
util.PushEndlessProgress(fmt.Sprintf(Conf.Language(111), util.EscapeHTML(tree.Root.IALAttr("title")))) util.PushEndlessProgress(fmt.Sprintf(Conf.Language(111), util.EscapeHTML(tree.Root.IALAttr("title"))))
if err = writeTreeUpsertQueue(tree); err != nil { if err = WriteTreeUpsertQueue(tree); err != nil {
util.ClearPushProgress(100) util.ClearPushProgress(100)
return return
} }

View file

@ -1553,7 +1553,7 @@ func (tx *Transaction) begin() (err error) {
func (tx *Transaction) commit() (err error) { func (tx *Transaction) commit() (err error) {
for _, tree := range tx.trees { for _, tree := range tx.trees {
if err = writeTreeUpsertQueue(tree); err != nil { if err = WriteTreeUpsertQueue(tree); err != nil {
return return
} }