transaction.go 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336
  1. // SiYuan - Refactor your thinking
  2. // Copyright (c) 2020-present, b3log.org
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. package model
  17. import (
  18. "bytes"
  19. "fmt"
  20. "path/filepath"
  21. "strings"
  22. "sync"
  23. "time"
  24. "github.com/88250/gulu"
  25. "github.com/88250/lute"
  26. "github.com/88250/lute/ast"
  27. "github.com/88250/lute/editor"
  28. "github.com/88250/lute/lex"
  29. "github.com/88250/lute/parse"
  30. "github.com/emirpasic/gods/sets/hashset"
  31. "github.com/siyuan-note/filelock"
  32. "github.com/siyuan-note/logging"
  33. "github.com/siyuan-note/siyuan/kernel/av"
  34. "github.com/siyuan-note/siyuan/kernel/cache"
  35. "github.com/siyuan-note/siyuan/kernel/filesys"
  36. "github.com/siyuan-note/siyuan/kernel/sql"
  37. "github.com/siyuan-note/siyuan/kernel/treenode"
  38. "github.com/siyuan-note/siyuan/kernel/util"
  39. )
  40. func IsFoldHeading(transactions *[]*Transaction) bool {
  41. for _, tx := range *transactions {
  42. for _, op := range tx.DoOperations {
  43. if "foldHeading" == op.Action {
  44. return true
  45. }
  46. }
  47. }
  48. return false
  49. }
  50. func IsUnfoldHeading(transactions *[]*Transaction) bool {
  51. for _, tx := range *transactions {
  52. for _, op := range tx.DoOperations {
  53. if "unfoldHeading" == op.Action {
  54. return true
  55. }
  56. }
  57. }
  58. return false
  59. }
  60. var (
  61. txQueue []*Transaction
  62. txQueueLock = sync.Mutex{}
  63. )
  64. func WaitForWritingFiles() {
  65. var printLog bool
  66. var lastPrintLog bool
  67. for i := 0; isWritingFiles(); i++ {
  68. time.Sleep(5 * time.Millisecond)
  69. if 2000 < i && !printLog { // 10s 后打日志
  70. logging.LogWarnf("file is writing: \n%s", logging.ShortStack())
  71. printLog = true
  72. }
  73. if 12000 < i && !lastPrintLog { // 60s 后打日志
  74. logging.LogWarnf("file is still writing")
  75. lastPrintLog = true
  76. }
  77. }
  78. }
  79. func isWritingFiles() bool {
  80. time.Sleep(time.Duration(20) * time.Millisecond)
  81. return 0 < len(txQueue) || util.IsMutexLocked(&txQueueLock) || util.IsMutexLocked(&flushLock)
  82. }
  83. func FlushTxJob() {
  84. flushTx()
  85. }
  86. var flushLock = sync.Mutex{}
  87. func flushTx() {
  88. defer logging.Recover()
  89. flushLock.Lock()
  90. defer flushLock.Unlock()
  91. currentTx := mergeTx()
  92. start := time.Now()
  93. if txErr := performTx(currentTx); nil != txErr {
  94. switch txErr.code {
  95. case TxErrCodeBlockNotFound:
  96. util.PushTxErr("Transaction failed", txErr.code, nil)
  97. return
  98. case TxErrCodeDataIsSyncing:
  99. util.PushErrMsg(Conf.Language(81), 5000)
  100. default:
  101. logging.LogFatalf(logging.ExitCodeFatal, "transaction failed: %s", txErr.msg)
  102. }
  103. }
  104. elapsed := time.Now().Sub(start).Milliseconds()
  105. if 0 < len(currentTx.DoOperations) {
  106. if 2000 < elapsed {
  107. logging.LogWarnf("op tx [%dms]", elapsed)
  108. }
  109. }
  110. }
  111. func mergeTx() (ret *Transaction) {
  112. txQueueLock.Lock()
  113. defer txQueueLock.Unlock()
  114. ret = &Transaction{}
  115. var doOps []*Operation
  116. for _, tx := range txQueue {
  117. for _, op := range tx.DoOperations {
  118. if l := len(doOps); 0 < l {
  119. lastOp := doOps[l-1]
  120. if "update" == lastOp.Action && "update" == op.Action && lastOp.ID == op.ID { // 连续相同的更新操作
  121. lastOp.discard = true
  122. }
  123. }
  124. doOps = append(doOps, op)
  125. }
  126. }
  127. for _, op := range doOps {
  128. if !op.discard {
  129. ret.DoOperations = append(ret.DoOperations, op)
  130. }
  131. }
  132. txQueue = nil
  133. return
  134. }
  135. func PerformTransactions(transactions *[]*Transaction) {
  136. txQueueLock.Lock()
  137. txQueue = append(txQueue, *transactions...)
  138. txQueueLock.Unlock()
  139. return
  140. }
  141. const (
  142. TxErrCodeBlockNotFound = 0
  143. TxErrCodeDataIsSyncing = 1
  144. TxErrCodeWriteTree = 2
  145. TxErrWriteAttributeView = 3
  146. )
  147. type TxErr struct {
  148. code int
  149. msg string
  150. id string
  151. }
  152. func performTx(tx *Transaction) (ret *TxErr) {
  153. if 1 > len(tx.DoOperations) {
  154. return
  155. }
  156. //os.MkdirAll("pprof", 0755)
  157. //cpuProfile, _ := os.Create("pprof/cpu_profile_tx")
  158. //pprof.StartCPUProfile(cpuProfile)
  159. //defer pprof.StopCPUProfile()
  160. var err error
  161. if err = tx.begin(); nil != err {
  162. if strings.Contains(err.Error(), "database is closed") {
  163. return
  164. }
  165. logging.LogErrorf("begin tx failed: %s", err)
  166. ret = &TxErr{msg: err.Error()}
  167. return
  168. }
  169. for _, op := range tx.DoOperations {
  170. switch op.Action {
  171. case "create":
  172. ret = tx.doCreate(op)
  173. case "update":
  174. ret = tx.doUpdate(op)
  175. case "insert":
  176. ret = tx.doInsert(op)
  177. case "delete":
  178. ret = tx.doDelete(op)
  179. case "move":
  180. ret = tx.doMove(op)
  181. case "append":
  182. ret = tx.doAppend(op)
  183. case "appendInsert":
  184. ret = tx.doAppendInsert(op)
  185. case "prependInsert":
  186. ret = tx.doPrependInsert(op)
  187. case "foldHeading":
  188. ret = tx.doFoldHeading(op)
  189. case "unfoldHeading":
  190. ret = tx.doUnfoldHeading(op)
  191. case "setAttrs":
  192. ret = tx.doSetAttrs(op)
  193. case "addFlashcards":
  194. ret = tx.doAddFlashcards(op)
  195. case "removeFlashcards":
  196. ret = tx.doRemoveFlashcards(op)
  197. case "setAttrViewName":
  198. ret = tx.doSetAttrViewName(op)
  199. case "setAttrViewFilters":
  200. ret = tx.doSetAttrViewFilters(op)
  201. case "setAttrViewSorts":
  202. ret = tx.doSetAttrViewSorts(op)
  203. case "setAttrViewColWidth":
  204. ret = tx.doSetAttrViewColumnWidth(op)
  205. case "setAttrViewColWrap":
  206. ret = tx.doSetAttrViewColumnWrap(op)
  207. case "setAttrViewColHidden":
  208. ret = tx.doSetAttrViewColumnHidden(op)
  209. case "insertAttrViewBlock":
  210. ret = tx.doInsertAttrViewBlock(op)
  211. case "removeAttrViewBlock":
  212. ret = tx.doRemoveAttrViewBlock(op)
  213. case "addAttrViewCol":
  214. ret = tx.doAddAttrViewColumn(op)
  215. case "updateAttrViewCol":
  216. ret = tx.doUpdateAttrViewColumn(op)
  217. case "removeAttrViewCol":
  218. ret = tx.doRemoveAttrViewColumn(op)
  219. case "sortAttrViewRow":
  220. ret = tx.doSortAttrViewRow(op)
  221. case "sortAttrViewCol":
  222. ret = tx.doSortAttrViewColumn(op)
  223. case "updateAttrViewCell":
  224. ret = tx.doUpdateAttrViewCell(op)
  225. case "updateAttrViewColOptions":
  226. ret = tx.doUpdateAttrViewColOptions(op)
  227. case "removeAttrViewColOption":
  228. ret = tx.doRemoveAttrViewColOption(op)
  229. case "updateAttrViewColOption":
  230. ret = tx.doUpdateAttrViewColOption(op)
  231. case "setAttrViewColCalc":
  232. ret = tx.doSetAttrViewColCalc(op)
  233. case "updateAttrViewColNumberFormat":
  234. ret = tx.doUpdateAttrViewColNumberFormat(op)
  235. }
  236. if nil != ret {
  237. tx.rollback()
  238. return
  239. }
  240. }
  241. if cr := tx.commit(); nil != cr {
  242. logging.LogErrorf("commit tx failed: %s", cr)
  243. return &TxErr{msg: cr.Error()}
  244. }
  245. return
  246. }
  247. func (tx *Transaction) doMove(operation *Operation) (ret *TxErr) {
  248. var err error
  249. id := operation.ID
  250. srcTree, err := tx.loadTree(id)
  251. if nil != err {
  252. logging.LogErrorf("load tree [%s] failed: %s", id, err)
  253. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  254. }
  255. srcNode := treenode.GetNodeInTree(srcTree, id)
  256. if nil == srcNode {
  257. logging.LogErrorf("get node [%s] in tree [%s] failed", id, srcTree.Root.ID)
  258. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  259. }
  260. var headingChildren []*ast.Node
  261. if isMovingFoldHeading := ast.NodeHeading == srcNode.Type && "1" == srcNode.IALAttr("fold"); isMovingFoldHeading {
  262. headingChildren = treenode.HeadingChildren(srcNode)
  263. // Blocks below other non-folded headings are no longer moved when moving a folded heading https://github.com/siyuan-note/siyuan/issues/8321
  264. headingChildren = treenode.GetHeadingFold(headingChildren)
  265. }
  266. var srcEmptyList *ast.Node
  267. if ast.NodeListItem == srcNode.Type && srcNode.Parent.FirstChild == srcNode && srcNode.Parent.LastChild == srcNode {
  268. // 列表中唯一的列表项被移除后,该列表就为空了
  269. srcEmptyList = srcNode.Parent
  270. }
  271. targetPreviousID := operation.PreviousID
  272. targetParentID := operation.ParentID
  273. if "" != targetPreviousID {
  274. if id == targetPreviousID {
  275. return
  276. }
  277. var targetTree *parse.Tree
  278. targetTree, err = tx.loadTree(targetPreviousID)
  279. if nil != err {
  280. logging.LogErrorf("load tree [%s] failed: %s", targetPreviousID, err)
  281. return &TxErr{code: TxErrCodeBlockNotFound, id: targetPreviousID}
  282. }
  283. isSameTree := srcTree.ID == targetTree.ID
  284. if isSameTree {
  285. targetTree = srcTree
  286. }
  287. targetNode := treenode.GetNodeInTree(targetTree, targetPreviousID)
  288. if nil == targetNode {
  289. logging.LogErrorf("get node [%s] in tree [%s] failed", targetPreviousID, targetTree.Root.ID)
  290. return &TxErr{code: TxErrCodeBlockNotFound, id: targetPreviousID}
  291. }
  292. if ast.NodeHeading == targetNode.Type && "1" == targetNode.IALAttr("fold") {
  293. targetChildren := treenode.HeadingChildren(targetNode)
  294. targetChildren = treenode.GetHeadingFold(targetChildren)
  295. if l := len(targetChildren); 0 < l {
  296. targetNode = targetChildren[l-1]
  297. }
  298. }
  299. if isMovingFoldHeadingIntoSelf(targetNode, headingChildren) {
  300. return
  301. }
  302. for i := len(headingChildren) - 1; -1 < i; i-- {
  303. c := headingChildren[i]
  304. targetNode.InsertAfter(c)
  305. }
  306. targetNode.InsertAfter(srcNode)
  307. if nil != srcEmptyList {
  308. srcEmptyList.Unlink()
  309. }
  310. refreshUpdated(srcNode)
  311. refreshUpdated(srcTree.Root)
  312. if err = tx.writeTree(srcTree); nil != err {
  313. return
  314. }
  315. if !isSameTree {
  316. if err = tx.writeTree(targetTree); nil != err {
  317. return
  318. }
  319. }
  320. return
  321. }
  322. if id == targetParentID {
  323. return
  324. }
  325. targetTree, err := tx.loadTree(targetParentID)
  326. if nil != err {
  327. logging.LogErrorf("load tree [%s] failed: %s", targetParentID, err)
  328. return &TxErr{code: TxErrCodeBlockNotFound, id: targetParentID}
  329. }
  330. isSameTree := srcTree.ID == targetTree.ID
  331. if isSameTree {
  332. targetTree = srcTree
  333. }
  334. targetNode := treenode.GetNodeInTree(targetTree, targetParentID)
  335. if nil == targetNode {
  336. logging.LogErrorf("get node [%s] in tree [%s] failed", targetParentID, targetTree.Root.ID)
  337. return &TxErr{code: TxErrCodeBlockNotFound, id: targetParentID}
  338. }
  339. if isMovingFoldHeadingIntoSelf(targetNode, headingChildren) {
  340. return
  341. }
  342. processed := false
  343. if ast.NodeSuperBlock == targetNode.Type {
  344. // 在布局节点后插入
  345. targetNode = targetNode.FirstChild.Next
  346. for i := len(headingChildren) - 1; -1 < i; i-- {
  347. c := headingChildren[i]
  348. targetNode.InsertAfter(c)
  349. }
  350. targetNode.InsertAfter(srcNode)
  351. if nil != srcEmptyList {
  352. srcEmptyList.Unlink()
  353. }
  354. processed = true
  355. } else if ast.NodeListItem == targetNode.Type {
  356. if 3 == targetNode.ListData.Typ {
  357. // 在任务列表标记节点后插入
  358. targetNode = targetNode.FirstChild
  359. for i := len(headingChildren) - 1; -1 < i; i-- {
  360. c := headingChildren[i]
  361. targetNode.InsertAfter(c)
  362. }
  363. targetNode.InsertAfter(srcNode)
  364. if nil != srcEmptyList {
  365. srcEmptyList.Unlink()
  366. }
  367. processed = true
  368. }
  369. }
  370. if !processed {
  371. for i := len(headingChildren) - 1; -1 < i; i-- {
  372. c := headingChildren[i]
  373. targetNode.PrependChild(c)
  374. }
  375. targetNode.PrependChild(srcNode)
  376. if nil != srcEmptyList {
  377. srcEmptyList.Unlink()
  378. }
  379. }
  380. refreshUpdated(srcNode)
  381. refreshUpdated(srcTree.Root)
  382. if err = tx.writeTree(srcTree); nil != err {
  383. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: id}
  384. }
  385. if !isSameTree {
  386. if err = tx.writeTree(targetTree); nil != err {
  387. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: id}
  388. }
  389. }
  390. return
  391. }
  392. func isMovingFoldHeadingIntoSelf(targetNode *ast.Node, headingChildren []*ast.Node) bool {
  393. for _, headingChild := range headingChildren {
  394. if headingChild.ID == targetNode.ID {
  395. // 不能将折叠标题移动到自己下方节点的前或后 https://github.com/siyuan-note/siyuan/issues/7163
  396. return true
  397. }
  398. }
  399. return false
  400. }
  401. func (tx *Transaction) doPrependInsert(operation *Operation) (ret *TxErr) {
  402. var err error
  403. block := treenode.GetBlockTree(operation.ParentID)
  404. if nil == block {
  405. msg := fmt.Sprintf("not found parent block [%s]", operation.ParentID)
  406. logging.LogErrorf(msg)
  407. return &TxErr{code: TxErrCodeBlockNotFound, id: operation.ParentID}
  408. }
  409. tree, err := tx.loadTree(block.ID)
  410. if nil != err {
  411. msg := fmt.Sprintf("load tree [%s] failed: %s", block.ID, err)
  412. logging.LogErrorf(msg)
  413. return &TxErr{code: TxErrCodeBlockNotFound, id: block.ID}
  414. }
  415. data := strings.ReplaceAll(operation.Data.(string), editor.FrontEndCaret, "")
  416. subTree := tx.luteEngine.BlockDOM2Tree(data)
  417. insertedNode := subTree.Root.FirstChild
  418. if nil == insertedNode {
  419. return &TxErr{code: TxErrCodeBlockNotFound, msg: "invalid data tree", id: block.ID}
  420. }
  421. var remains []*ast.Node
  422. for remain := insertedNode.Next; nil != remain; remain = remain.Next {
  423. if ast.NodeKramdownBlockIAL != remain.Type {
  424. if "" == remain.ID {
  425. remain.ID = ast.NewNodeID()
  426. remain.SetIALAttr("id", remain.ID)
  427. }
  428. remains = append(remains, remain)
  429. }
  430. }
  431. if "" == insertedNode.ID {
  432. insertedNode.ID = ast.NewNodeID()
  433. insertedNode.SetIALAttr("id", insertedNode.ID)
  434. }
  435. node := treenode.GetNodeInTree(tree, operation.ParentID)
  436. if nil == node {
  437. logging.LogErrorf("get node [%s] in tree [%s] failed", operation.ParentID, tree.Root.ID)
  438. return &TxErr{code: TxErrCodeBlockNotFound, id: operation.ParentID}
  439. }
  440. isContainer := node.IsContainerBlock()
  441. for i := len(remains) - 1; 0 <= i; i-- {
  442. remain := remains[i]
  443. if isContainer {
  444. if ast.NodeListItem == node.Type && 3 == node.ListData.Typ {
  445. node.FirstChild.InsertAfter(remain)
  446. } else if ast.NodeSuperBlock == node.Type {
  447. node.FirstChild.Next.InsertAfter(remain)
  448. } else {
  449. node.PrependChild(remain)
  450. }
  451. } else {
  452. node.InsertAfter(remain)
  453. }
  454. }
  455. if isContainer {
  456. if ast.NodeListItem == node.Type && 3 == node.ListData.Typ {
  457. node.FirstChild.InsertAfter(insertedNode)
  458. } else if ast.NodeSuperBlock == node.Type {
  459. node.FirstChild.Next.InsertAfter(insertedNode)
  460. } else {
  461. node.PrependChild(insertedNode)
  462. }
  463. } else {
  464. node.InsertAfter(insertedNode)
  465. }
  466. createdUpdated(insertedNode)
  467. tx.nodes[insertedNode.ID] = insertedNode
  468. if err = tx.writeTree(tree); nil != err {
  469. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: block.ID}
  470. }
  471. operation.ID = insertedNode.ID
  472. operation.ParentID = insertedNode.Parent.ID
  473. // 将 prependInsert 转换为 insert 推送
  474. operation.Action = "insert"
  475. if nil != insertedNode.Previous {
  476. operation.PreviousID = insertedNode.Previous.ID
  477. }
  478. return
  479. }
  480. func (tx *Transaction) doAppendInsert(operation *Operation) (ret *TxErr) {
  481. var err error
  482. block := treenode.GetBlockTree(operation.ParentID)
  483. if nil == block {
  484. msg := fmt.Sprintf("not found parent block [%s]", operation.ParentID)
  485. logging.LogErrorf(msg)
  486. return &TxErr{code: TxErrCodeBlockNotFound, id: operation.ParentID}
  487. }
  488. tree, err := tx.loadTree(block.ID)
  489. if nil != err {
  490. msg := fmt.Sprintf("load tree [%s] failed: %s", block.ID, err)
  491. logging.LogErrorf(msg)
  492. return &TxErr{code: TxErrCodeBlockNotFound, id: block.ID}
  493. }
  494. data := strings.ReplaceAll(operation.Data.(string), editor.FrontEndCaret, "")
  495. subTree := tx.luteEngine.BlockDOM2Tree(data)
  496. insertedNode := subTree.Root.FirstChild
  497. if nil == insertedNode {
  498. return &TxErr{code: TxErrCodeBlockNotFound, msg: "invalid data tree", id: block.ID}
  499. }
  500. if "" == insertedNode.ID {
  501. insertedNode.ID = ast.NewNodeID()
  502. insertedNode.SetIALAttr("id", insertedNode.ID)
  503. }
  504. var toInserts []*ast.Node
  505. for toInsert := insertedNode; nil != toInsert; toInsert = toInsert.Next {
  506. if ast.NodeKramdownBlockIAL != toInsert.Type {
  507. if "" == toInsert.ID {
  508. toInsert.ID = ast.NewNodeID()
  509. toInsert.SetIALAttr("id", toInsert.ID)
  510. }
  511. toInserts = append(toInserts, toInsert)
  512. }
  513. }
  514. node := treenode.GetNodeInTree(tree, operation.ParentID)
  515. if nil == node {
  516. logging.LogErrorf("get node [%s] in tree [%s] failed", operation.ParentID, tree.Root.ID)
  517. return &TxErr{code: TxErrCodeBlockNotFound, id: operation.ParentID}
  518. }
  519. isContainer := node.IsContainerBlock()
  520. for i := 0; i < len(toInserts); i++ {
  521. toInsert := toInserts[i]
  522. if isContainer {
  523. if ast.NodeSuperBlock == node.Type {
  524. node.LastChild.InsertBefore(toInsert)
  525. } else {
  526. node.AppendChild(toInsert)
  527. }
  528. } else {
  529. node.InsertAfter(toInsert)
  530. }
  531. }
  532. createdUpdated(insertedNode)
  533. tx.nodes[insertedNode.ID] = insertedNode
  534. if err = tx.writeTree(tree); nil != err {
  535. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: block.ID}
  536. }
  537. operation.ID = insertedNode.ID
  538. operation.ParentID = insertedNode.Parent.ID
  539. // 将 appendInsert 转换为 insert 推送
  540. operation.Action = "insert"
  541. if nil != insertedNode.Previous {
  542. operation.PreviousID = insertedNode.Previous.ID
  543. }
  544. return
  545. }
  546. func (tx *Transaction) doAppend(operation *Operation) (ret *TxErr) {
  547. var err error
  548. id := operation.ID
  549. srcTree, err := tx.loadTree(id)
  550. if nil != err {
  551. logging.LogErrorf("load tree [%s] failed: %s", id, err)
  552. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  553. }
  554. srcNode := treenode.GetNodeInTree(srcTree, id)
  555. if nil == srcNode {
  556. logging.LogErrorf("get node [%s] in tree [%s] failed", id, srcTree.Root.ID)
  557. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  558. }
  559. if ast.NodeDocument == srcNode.Type {
  560. logging.LogWarnf("can't append a root to another root")
  561. return
  562. }
  563. var headingChildren []*ast.Node
  564. if isMovingFoldHeading := ast.NodeHeading == srcNode.Type && "1" == srcNode.IALAttr("fold"); isMovingFoldHeading {
  565. headingChildren = treenode.HeadingChildren(srcNode)
  566. }
  567. var srcEmptyList, targetNewList *ast.Node
  568. if ast.NodeListItem == srcNode.Type {
  569. targetNewListID := ast.NewNodeID()
  570. targetNewList = &ast.Node{ID: targetNewListID, Type: ast.NodeList, ListData: &ast.ListData{Typ: srcNode.ListData.Typ}}
  571. targetNewList.SetIALAttr("id", targetNewListID)
  572. if srcNode.Parent.FirstChild == srcNode && srcNode.Parent.LastChild == srcNode {
  573. // 列表中唯一的列表项被移除后,该列表就为空了
  574. srcEmptyList = srcNode.Parent
  575. }
  576. }
  577. targetRootID := operation.ParentID
  578. if id == targetRootID {
  579. logging.LogWarnf("target root id is nil")
  580. return
  581. }
  582. targetTree, err := tx.loadTree(targetRootID)
  583. if nil != err {
  584. logging.LogErrorf("load tree [%s] failed: %s", targetRootID, err)
  585. return &TxErr{code: TxErrCodeBlockNotFound, id: targetRootID}
  586. }
  587. isSameTree := srcTree.ID == targetTree.ID
  588. if isSameTree {
  589. targetTree = srcTree
  590. }
  591. targetRoot := targetTree.Root
  592. if nil != targetNewList {
  593. if nil != targetRoot.LastChild {
  594. if ast.NodeList != targetRoot.LastChild.Type {
  595. targetNewList.AppendChild(srcNode)
  596. targetRoot.AppendChild(targetNewList)
  597. } else {
  598. targetRoot.LastChild.AppendChild(srcNode)
  599. }
  600. } else {
  601. targetRoot.AppendChild(srcNode)
  602. }
  603. } else {
  604. targetRoot.AppendChild(srcNode)
  605. }
  606. for _, c := range headingChildren {
  607. targetRoot.AppendChild(c)
  608. }
  609. if nil != srcEmptyList {
  610. srcEmptyList.Unlink()
  611. }
  612. if err = tx.writeTree(srcTree); nil != err {
  613. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: id}
  614. }
  615. if !isSameTree {
  616. if err = tx.writeTree(targetTree); nil != err {
  617. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: id}
  618. }
  619. }
  620. return
  621. }
  622. func (tx *Transaction) doDelete(operation *Operation) (ret *TxErr) {
  623. // logging.LogInfof("commit delete [%+v]", operation)
  624. var err error
  625. id := operation.ID
  626. tree, err := tx.loadTree(id)
  627. if ErrBlockNotFound == err {
  628. return nil // move 以后这里会空,算作正常情况
  629. }
  630. if nil != err {
  631. msg := fmt.Sprintf("load tree [%s] failed: %s", id, err)
  632. logging.LogErrorf(msg)
  633. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  634. }
  635. node := treenode.GetNodeInTree(tree, id)
  636. if nil == node {
  637. return nil // move 以后的情况,列表项移动导致的状态异常 https://github.com/siyuan-note/insider/issues/961
  638. }
  639. parent := node.Parent
  640. if nil != node.Next && ast.NodeKramdownBlockIAL == node.Next.Type && bytes.Contains(node.Next.Tokens, []byte(node.ID)) {
  641. // 列表块撤销状态异常 https://github.com/siyuan-note/siyuan/issues/3985
  642. node.Next.Unlink()
  643. }
  644. node.Unlink()
  645. if nil != parent && ast.NodeListItem == parent.Type && nil == parent.FirstChild {
  646. // 保持空列表项
  647. node.FirstChild = nil
  648. parent.AppendChild(node)
  649. }
  650. treenode.RemoveBlockTree(node.ID)
  651. delete(tx.nodes, node.ID)
  652. if err = tx.writeTree(tree); nil != err {
  653. return
  654. }
  655. syncDelete2AttributeView(node)
  656. return
  657. }
  658. func syncDelete2AttributeView(node *ast.Node) {
  659. avs := node.IALAttr(NodeAttrNameAvs)
  660. if "" == avs {
  661. return
  662. }
  663. avIDs := strings.Split(avs, ",")
  664. for _, avID := range avIDs {
  665. attrView, parseErr := av.ParseAttributeView(avID)
  666. if nil != parseErr {
  667. continue
  668. }
  669. changedAv := false
  670. blockValues := attrView.GetBlockKeyValues()
  671. if nil == blockValues {
  672. continue
  673. }
  674. for i, blockValue := range blockValues.Values {
  675. if blockValue.Block.ID == node.ID {
  676. blockValues.Values = append(blockValues.Values[:i], blockValues.Values[i+1:]...)
  677. changedAv = true
  678. break
  679. }
  680. }
  681. if changedAv {
  682. av.SaveAttributeView(attrView)
  683. util.BroadcastByType("protyle", "refreshAttributeView", 0, "", map[string]interface{}{"id": avID})
  684. }
  685. }
  686. }
  687. func (tx *Transaction) doInsert(operation *Operation) (ret *TxErr) {
  688. var err error
  689. opParentID := operation.ParentID
  690. block := treenode.GetBlockTree(opParentID)
  691. if nil == block {
  692. block = treenode.GetBlockTree(operation.PreviousID)
  693. if nil == block {
  694. block = treenode.GetBlockTree(operation.NextID)
  695. }
  696. }
  697. if nil == block {
  698. msg := fmt.Sprintf("not found next block [%s]", operation.NextID)
  699. logging.LogErrorf(msg)
  700. return &TxErr{code: TxErrCodeBlockNotFound, id: operation.NextID}
  701. }
  702. tree, err := tx.loadTree(block.ID)
  703. if nil != err {
  704. msg := fmt.Sprintf("load tree [%s] failed: %s", block.ID, err)
  705. logging.LogErrorf(msg)
  706. return &TxErr{code: TxErrCodeBlockNotFound, id: block.ID}
  707. }
  708. data := strings.ReplaceAll(operation.Data.(string), editor.FrontEndCaret, "")
  709. subTree := tx.luteEngine.BlockDOM2Tree(data)
  710. p := block.Path
  711. assets := getAssetsDir(filepath.Join(util.DataDir, block.BoxID), filepath.Dir(filepath.Join(util.DataDir, block.BoxID, p)))
  712. isGlobalAssets := strings.HasPrefix(assets, filepath.Join(util.DataDir, "assets"))
  713. if !isGlobalAssets {
  714. // 本地资源文件需要移动到用户手动建立的 assets 下 https://github.com/siyuan-note/siyuan/issues/2410
  715. ast.Walk(subTree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
  716. if !entering {
  717. return ast.WalkContinue
  718. }
  719. if ast.NodeLinkDest == n.Type && bytes.HasPrefix(n.Tokens, []byte("assets/")) {
  720. assetP := gulu.Str.FromBytes(n.Tokens)
  721. assetPath, e := GetAssetAbsPath(assetP)
  722. if nil != e {
  723. logging.LogErrorf("get path of asset [%s] failed: %s", assetP, err)
  724. return ast.WalkContinue
  725. }
  726. if !strings.HasPrefix(assetPath, filepath.Join(util.DataDir, "assets")) {
  727. // 非全局 assets 则跳过
  728. return ast.WalkContinue
  729. }
  730. // 只有全局 assets 才移动到相对 assets
  731. targetP := filepath.Join(assets, filepath.Base(assetPath))
  732. if e = filelock.Move(assetPath, targetP); nil != err {
  733. logging.LogErrorf("copy path of asset from [%s] to [%s] failed: %s", assetPath, targetP, err)
  734. return ast.WalkContinue
  735. }
  736. }
  737. return ast.WalkContinue
  738. })
  739. }
  740. insertedNode := subTree.Root.FirstChild
  741. if nil == insertedNode {
  742. return &TxErr{code: TxErrCodeBlockNotFound, msg: "invalid data tree", id: block.ID}
  743. }
  744. var remains []*ast.Node
  745. for remain := insertedNode.Next; nil != remain; remain = remain.Next {
  746. if ast.NodeKramdownBlockIAL != remain.Type {
  747. if "" == remain.ID {
  748. remain.ID = ast.NewNodeID()
  749. remain.SetIALAttr("id", remain.ID)
  750. }
  751. remains = append(remains, remain)
  752. }
  753. }
  754. if "" == insertedNode.ID {
  755. insertedNode.ID = ast.NewNodeID()
  756. insertedNode.SetIALAttr("id", insertedNode.ID)
  757. }
  758. var node *ast.Node
  759. nextID := operation.NextID
  760. previousID := operation.PreviousID
  761. if "" != nextID {
  762. node = treenode.GetNodeInTree(tree, nextID)
  763. if nil == node {
  764. logging.LogErrorf("get node [%s] in tree [%s] failed", nextID, tree.Root.ID)
  765. return &TxErr{code: TxErrCodeBlockNotFound, id: nextID}
  766. }
  767. if ast.NodeList == insertedNode.Type && nil != node.Parent && ast.NodeList == node.Parent.Type {
  768. insertedNode = insertedNode.FirstChild
  769. }
  770. node.InsertBefore(insertedNode)
  771. } else if "" != previousID {
  772. node = treenode.GetNodeInTree(tree, previousID)
  773. if nil == node {
  774. logging.LogErrorf("get node [%s] in tree [%s] failed", previousID, tree.Root.ID)
  775. return &TxErr{code: TxErrCodeBlockNotFound, id: previousID}
  776. }
  777. if ast.NodeHeading == node.Type && "1" == node.IALAttr("fold") {
  778. children := treenode.HeadingChildren(node)
  779. if l := len(children); 0 < l {
  780. node = children[l-1]
  781. }
  782. }
  783. if ast.NodeList == insertedNode.Type && nil != node.Parent && ast.NodeList == node.Parent.Type {
  784. insertedNode = insertedNode.FirstChild
  785. }
  786. for i := len(remains) - 1; 0 <= i; i-- {
  787. remain := remains[i]
  788. node.InsertAfter(remain)
  789. }
  790. node.InsertAfter(insertedNode)
  791. } else {
  792. node = treenode.GetNodeInTree(tree, operation.ParentID)
  793. if nil == node {
  794. logging.LogErrorf("get node [%s] in tree [%s] failed", operation.ParentID, tree.Root.ID)
  795. return &TxErr{code: TxErrCodeBlockNotFound, id: operation.ParentID}
  796. }
  797. if ast.NodeSuperBlock == node.Type {
  798. // 在布局节点后插入
  799. node.FirstChild.Next.InsertAfter(insertedNode)
  800. } else {
  801. if ast.NodeList == insertedNode.Type && nil != insertedNode.FirstChild && operation.ID == insertedNode.FirstChild.ID && operation.ID != insertedNode.ID {
  802. // 将一个列表项移动到另一个列表的第一项时 https://github.com/siyuan-note/siyuan/issues/2341
  803. insertedNode = insertedNode.FirstChild
  804. }
  805. if ast.NodeListItem == node.Type && 3 == node.ListData.Typ {
  806. // 在任务列表标记节点后插入
  807. node.FirstChild.InsertAfter(insertedNode)
  808. for _, remain := range remains {
  809. node.FirstChild.InsertAfter(remain)
  810. }
  811. } else {
  812. for i := len(remains) - 1; 0 <= i; i-- {
  813. remain := remains[i]
  814. node.PrependChild(remain)
  815. }
  816. node.PrependChild(insertedNode)
  817. }
  818. }
  819. }
  820. createdUpdated(insertedNode)
  821. tx.nodes[insertedNode.ID] = insertedNode
  822. if err = tx.writeTree(tree); nil != err {
  823. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: block.ID}
  824. }
  825. operation.ID = insertedNode.ID
  826. operation.ParentID = insertedNode.Parent.ID
  827. return
  828. }
  829. func (tx *Transaction) doUpdate(operation *Operation) (ret *TxErr) {
  830. id := operation.ID
  831. tree, err := tx.loadTree(id)
  832. if nil != err {
  833. logging.LogErrorf("load tree [%s] failed: %s", id, err)
  834. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  835. }
  836. data := strings.ReplaceAll(operation.Data.(string), editor.FrontEndCaret, "")
  837. if "" == data {
  838. logging.LogErrorf("update data is nil")
  839. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  840. }
  841. subTree := tx.luteEngine.BlockDOM2Tree(data)
  842. subTree.ID, subTree.Box, subTree.Path = tree.ID, tree.Box, tree.Path
  843. oldNode := treenode.GetNodeInTree(tree, id)
  844. if nil == oldNode {
  845. logging.LogErrorf("get node [%s] in tree [%s] failed", id, tree.Root.ID)
  846. return &TxErr{msg: ErrBlockNotFound.Error(), id: id}
  847. }
  848. var unlinks []*ast.Node
  849. ast.Walk(subTree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
  850. if !entering {
  851. return ast.WalkContinue
  852. }
  853. if ast.NodeTextMark == n.Type {
  854. if n.IsTextMarkType("inline-math") {
  855. if "" == strings.TrimSpace(n.TextMarkInlineMathContent) {
  856. // 剔除空白的行级公式
  857. unlinks = append(unlinks, n)
  858. }
  859. } else if n.IsTextMarkType("block-ref") {
  860. sql.CacheRef(subTree, n)
  861. if "d" == n.TextMarkBlockRefSubtype {
  862. // 偶发编辑文档标题后引用处的动态锚文本不更新 https://github.com/siyuan-note/siyuan/issues/5891
  863. // 使用缓存的动态锚文本强制覆盖当前块中的引用节点动态锚文本
  864. if dRefText, ok := treenode.DynamicRefTexts.Load(n.TextMarkBlockRefID); ok && "" != dRefText {
  865. n.TextMarkTextContent = dRefText.(string)
  866. }
  867. }
  868. }
  869. }
  870. return ast.WalkContinue
  871. })
  872. for _, n := range unlinks {
  873. n.Unlink()
  874. }
  875. updatedNode := subTree.Root.FirstChild
  876. if nil == updatedNode {
  877. logging.LogErrorf("get fist node in sub tree [%s] failed", subTree.Root.ID)
  878. return &TxErr{msg: ErrBlockNotFound.Error(), id: id}
  879. }
  880. if ast.NodeList == updatedNode.Type && ast.NodeList == oldNode.Parent.Type {
  881. updatedNode = updatedNode.FirstChild
  882. }
  883. if oldNode.IsContainerBlock() {
  884. // 更新容器块的话需要考虑其子块中可能存在的折叠标题,需要把这些折叠标题的下方块移动到新节点下面
  885. treenode.MoveFoldHeading(updatedNode, oldNode)
  886. }
  887. cache.PutBlockIAL(updatedNode.ID, parse.IAL2Map(updatedNode.KramdownIAL))
  888. // 替换为新节点
  889. oldNode.InsertAfter(updatedNode)
  890. oldNode.Unlink()
  891. createdUpdated(updatedNode)
  892. tx.nodes[updatedNode.ID] = updatedNode
  893. if err = tx.writeTree(tree); nil != err {
  894. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: id}
  895. }
  896. return
  897. }
  898. func (tx *Transaction) doCreate(operation *Operation) (ret *TxErr) {
  899. tree := operation.Data.(*parse.Tree)
  900. tx.writeTree(tree)
  901. return
  902. }
  903. func (tx *Transaction) doSetAttrs(operation *Operation) (ret *TxErr) {
  904. id := operation.ID
  905. tree, err := tx.loadTree(id)
  906. if nil != err {
  907. logging.LogErrorf("load tree [%s] failed: %s", id, err)
  908. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  909. }
  910. node := treenode.GetNodeInTree(tree, id)
  911. if nil == node {
  912. logging.LogErrorf("get node [%s] in tree [%s] failed", id, tree.Root.ID)
  913. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  914. }
  915. attrs := map[string]string{}
  916. if err = gulu.JSON.UnmarshalJSON([]byte(operation.Data.(string)), &attrs); nil != err {
  917. logging.LogErrorf("unmarshal attrs failed: %s", err)
  918. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  919. }
  920. var invalidNames []string
  921. for name := range attrs {
  922. for i := 0; i < len(name); i++ {
  923. if !lex.IsASCIILetterNumHyphen(name[i]) {
  924. logging.LogWarnf("invalid attr name [%s]", name)
  925. invalidNames = append(invalidNames, name)
  926. }
  927. }
  928. }
  929. for _, name := range invalidNames {
  930. delete(attrs, name)
  931. }
  932. for name, value := range attrs {
  933. if "" == value {
  934. node.RemoveIALAttr(name)
  935. } else {
  936. node.SetIALAttr(name, value)
  937. }
  938. }
  939. if err = tx.writeTree(tree); nil != err {
  940. return
  941. }
  942. cache.PutBlockIAL(id, parse.IAL2Map(node.KramdownIAL))
  943. return
  944. }
  945. func refreshUpdated(node *ast.Node) {
  946. updated := util.CurrentTimeSecondsStr()
  947. node.SetIALAttr("updated", updated)
  948. parents := treenode.ParentNodes(node)
  949. for _, parent := range parents { // 更新所有父节点的更新时间字段
  950. parent.SetIALAttr("updated", updated)
  951. }
  952. }
  953. func createdUpdated(node *ast.Node) {
  954. created := util.TimeFromID(node.ID)
  955. updated := node.IALAttr("updated")
  956. if "" == updated {
  957. updated = created
  958. }
  959. if updated < created {
  960. updated = created // 复制粘贴块后创建时间小于更新时间 https://github.com/siyuan-note/siyuan/issues/3624
  961. }
  962. parents := treenode.ParentNodes(node)
  963. for _, parent := range parents { // 更新所有父节点的更新时间字段
  964. parent.SetIALAttr("updated", updated)
  965. }
  966. }
  967. type Operation struct {
  968. Action string `json:"action"`
  969. Data interface{} `json:"data"`
  970. ID string `json:"id"`
  971. ParentID string `json:"parentID"`
  972. PreviousID string `json:"previousID"`
  973. NextID string `json:"nextID"`
  974. RetData interface{} `json:"retData"`
  975. BlockIDs []string `json:"blockIDs"`
  976. DeckID string `json:"deckID"` // 用于添加/删除闪卡
  977. AvID string `json:"avID"` // 属性视图 ID
  978. SrcIDs []string `json:"srcIDs"` // 用于将块拖拽到属性视图中
  979. Name string `json:"name"` // 属性视图列名
  980. Typ string `json:"type"` // 属性视图列类型
  981. Format string `json:"format"` // 属性视图列格式化
  982. KeyID string `json:"keyID"` // 属性视列 ID
  983. RowID string `json:"rowID"` // 属性视图行 ID
  984. discard bool // 用于标识是否在事务合并中丢弃
  985. }
  986. type Transaction struct {
  987. DoOperations []*Operation `json:"doOperations"`
  988. UndoOperations []*Operation `json:"undoOperations"`
  989. trees map[string]*parse.Tree
  990. nodes map[string]*ast.Node
  991. luteEngine *lute.Lute
  992. }
  993. func (tx *Transaction) begin() (err error) {
  994. if nil != err {
  995. return
  996. }
  997. tx.trees = map[string]*parse.Tree{}
  998. tx.nodes = map[string]*ast.Node{}
  999. tx.luteEngine = util.NewLute()
  1000. return
  1001. }
  1002. func (tx *Transaction) commit() (err error) {
  1003. for _, tree := range tx.trees {
  1004. if err = writeJSONQueue(tree); nil != err {
  1005. return
  1006. }
  1007. }
  1008. refreshDynamicRefTexts(tx.nodes, tx.trees)
  1009. IncSync()
  1010. tx.trees = nil
  1011. return
  1012. }
  1013. func (tx *Transaction) rollback() {
  1014. tx.trees, tx.nodes = nil, nil
  1015. return
  1016. }
  1017. func (tx *Transaction) loadTree(id string) (ret *parse.Tree, err error) {
  1018. var rootID, box, p string
  1019. bt := treenode.GetBlockTree(id)
  1020. if nil == bt {
  1021. return nil, ErrBlockNotFound
  1022. }
  1023. rootID = bt.RootID
  1024. box = bt.BoxID
  1025. p = bt.Path
  1026. ret = tx.trees[rootID]
  1027. if nil != ret {
  1028. return
  1029. }
  1030. ret, err = filesys.LoadTree(box, p, tx.luteEngine)
  1031. if nil != err {
  1032. return
  1033. }
  1034. tx.trees[rootID] = ret
  1035. return
  1036. }
  1037. func (tx *Transaction) writeTree(tree *parse.Tree) (err error) {
  1038. tx.trees[tree.ID] = tree
  1039. treenode.IndexBlockTree(tree)
  1040. return
  1041. }
  1042. // refreshDynamicRefText 用于刷新引用块的动态锚文本。
  1043. // 该实现依赖了数据库缓存,导致外部调用时可能需要阻塞等待数据库写入后才能获取到 refs
  1044. func refreshDynamicRefText(updatedDefNode *ast.Node, updatedTree *parse.Tree) {
  1045. changedDefs := map[string]*ast.Node{updatedDefNode.ID: updatedDefNode}
  1046. changedTrees := map[string]*parse.Tree{updatedTree.ID: updatedTree}
  1047. refreshDynamicRefTexts(changedDefs, changedTrees)
  1048. }
  1049. // refreshDynamicRefTexts 用于批量刷新引用块的动态锚文本。
  1050. // 该实现依赖了数据库缓存,导致外部调用时可能需要阻塞等待数据库写入后才能获取到 refs
  1051. func refreshDynamicRefTexts(updatedDefNodes map[string]*ast.Node, updatedTrees map[string]*parse.Tree) {
  1052. // 1. 更新引用的动态锚文本
  1053. treeRefNodeIDs := map[string]*hashset.Set{}
  1054. for _, updateNode := range updatedDefNodes {
  1055. refs := sql.GetRefsCacheByDefID(updateNode.ID)
  1056. if nil != updateNode.Parent && ast.NodeDocument != updateNode.Parent.Type &&
  1057. updateNode.Parent.IsContainerBlock() && (updateNode == treenode.FirstLeafBlock(updateNode.Parent)) { // 容器块下第一个子块
  1058. var parentRefs []*sql.Ref
  1059. if ast.NodeListItem == updateNode.Parent.Type { // 引用列表块时动态锚文本未跟随定义块内容变动 https://github.com/siyuan-note/siyuan/issues/4393
  1060. parentRefs = sql.GetRefsCacheByDefID(updateNode.Parent.Parent.ID)
  1061. updatedDefNodes[updateNode.Parent.ID] = updateNode.Parent
  1062. updatedDefNodes[updateNode.Parent.Parent.ID] = updateNode.Parent.Parent
  1063. } else {
  1064. parentRefs = sql.GetRefsCacheByDefID(updateNode.Parent.ID)
  1065. updatedDefNodes[updateNode.Parent.ID] = updateNode.Parent
  1066. }
  1067. if 0 < len(parentRefs) {
  1068. refs = append(refs, parentRefs...)
  1069. }
  1070. }
  1071. for _, ref := range refs {
  1072. if refIDs, ok := treeRefNodeIDs[ref.RootID]; !ok {
  1073. refIDs = hashset.New()
  1074. refIDs.Add(ref.BlockID)
  1075. treeRefNodeIDs[ref.RootID] = refIDs
  1076. } else {
  1077. refIDs.Add(ref.BlockID)
  1078. }
  1079. }
  1080. }
  1081. changedRefTree := map[string]*parse.Tree{}
  1082. for refTreeID, refNodeIDs := range treeRefNodeIDs {
  1083. refTree, ok := updatedTrees[refTreeID]
  1084. if !ok {
  1085. var err error
  1086. refTree, err = loadTreeByBlockID(refTreeID)
  1087. if nil != err {
  1088. continue
  1089. }
  1090. }
  1091. var refTreeChanged bool
  1092. ast.Walk(refTree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
  1093. if !entering {
  1094. return ast.WalkContinue
  1095. }
  1096. if n.IsBlock() && refNodeIDs.Contains(n.ID) {
  1097. changed := updateRefText(n, updatedDefNodes)
  1098. if !refTreeChanged && changed {
  1099. refTreeChanged = true
  1100. }
  1101. return ast.WalkContinue
  1102. }
  1103. return ast.WalkContinue
  1104. })
  1105. if refTreeChanged {
  1106. changedRefTree[refTreeID] = refTree
  1107. }
  1108. }
  1109. // 2. 更新属性视图主键内容
  1110. for _, updatedDefNode := range updatedDefNodes {
  1111. avs := updatedDefNode.IALAttr(NodeAttrNameAvs)
  1112. if "" == avs {
  1113. continue
  1114. }
  1115. avIDs := strings.Split(avs, ",")
  1116. for _, avID := range avIDs {
  1117. attrView, parseErr := av.ParseAttributeView(avID)
  1118. if nil != parseErr {
  1119. continue
  1120. }
  1121. changedAv := false
  1122. blockValues := attrView.GetBlockKeyValues()
  1123. if nil == blockValues {
  1124. continue
  1125. }
  1126. for _, blockValue := range blockValues.Values {
  1127. if blockValue.Block.ID == updatedDefNode.ID {
  1128. newContent := getNodeRefText(updatedDefNode)
  1129. if newContent != blockValue.Block.Content {
  1130. blockValue.Block.Content = newContent
  1131. changedAv = true
  1132. }
  1133. break
  1134. }
  1135. }
  1136. if changedAv {
  1137. av.SaveAttributeView(attrView)
  1138. util.BroadcastByType("protyle", "refreshAttributeView", 0, "", map[string]interface{}{"id": avID})
  1139. }
  1140. }
  1141. }
  1142. // 3. 保存变更
  1143. for _, tree := range changedRefTree {
  1144. indexWriteJSONQueue(tree)
  1145. }
  1146. }
  1147. var updateRefTextRenameDocs = map[string]*parse.Tree{}
  1148. var updateRefTextRenameDocLock = sync.Mutex{}
  1149. func updateRefTextRenameDoc(renamedTree *parse.Tree) {
  1150. updateRefTextRenameDocLock.Lock()
  1151. updateRefTextRenameDocs[renamedTree.ID] = renamedTree
  1152. updateRefTextRenameDocLock.Unlock()
  1153. }
  1154. func FlushUpdateRefTextRenameDocJob() {
  1155. sql.WaitForWritingDatabase()
  1156. flushUpdateRefTextRenameDoc()
  1157. }
  1158. func flushUpdateRefTextRenameDoc() {
  1159. updateRefTextRenameDocLock.Lock()
  1160. defer updateRefTextRenameDocLock.Unlock()
  1161. for _, tree := range updateRefTextRenameDocs {
  1162. refreshDynamicRefText(tree.Root, tree)
  1163. }
  1164. updateRefTextRenameDocs = map[string]*parse.Tree{}
  1165. }
  1166. func updateRefText(refNode *ast.Node, changedDefNodes map[string]*ast.Node) (changed bool) {
  1167. ast.Walk(refNode, func(n *ast.Node, entering bool) ast.WalkStatus {
  1168. if !entering {
  1169. return ast.WalkContinue
  1170. }
  1171. if !treenode.IsBlockRef(n) {
  1172. return ast.WalkContinue
  1173. }
  1174. defID, _, subtype := treenode.GetBlockRef(n)
  1175. if "s" == subtype || "" == defID {
  1176. return ast.WalkContinue
  1177. }
  1178. defNode := changedDefNodes[defID]
  1179. if nil == defNode {
  1180. return ast.WalkSkipChildren
  1181. }
  1182. refText := getNodeRefText(defNode)
  1183. treenode.SetDynamicBlockRefText(n, refText)
  1184. changed = true
  1185. return ast.WalkContinue
  1186. })
  1187. return
  1188. }