transaction.go 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559
  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. "errors"
  20. "fmt"
  21. "path/filepath"
  22. "runtime/debug"
  23. "strings"
  24. "sync"
  25. "sync/atomic"
  26. "time"
  27. "github.com/88250/gulu"
  28. "github.com/88250/lute"
  29. "github.com/88250/lute/ast"
  30. "github.com/88250/lute/editor"
  31. "github.com/88250/lute/lex"
  32. "github.com/88250/lute/parse"
  33. "github.com/emirpasic/gods/sets/hashset"
  34. "github.com/siyuan-note/filelock"
  35. "github.com/siyuan-note/logging"
  36. "github.com/siyuan-note/siyuan/kernel/av"
  37. "github.com/siyuan-note/siyuan/kernel/cache"
  38. "github.com/siyuan-note/siyuan/kernel/filesys"
  39. "github.com/siyuan-note/siyuan/kernel/sql"
  40. "github.com/siyuan-note/siyuan/kernel/treenode"
  41. "github.com/siyuan-note/siyuan/kernel/util"
  42. )
  43. func IsFoldHeading(transactions *[]*Transaction) bool {
  44. for _, tx := range *transactions {
  45. for _, op := range tx.DoOperations {
  46. if "foldHeading" == op.Action {
  47. return true
  48. }
  49. }
  50. }
  51. return false
  52. }
  53. func IsUnfoldHeading(transactions *[]*Transaction) bool {
  54. for _, tx := range *transactions {
  55. for _, op := range tx.DoOperations {
  56. if "unfoldHeading" == op.Action {
  57. return true
  58. }
  59. }
  60. }
  61. return false
  62. }
  63. func IsMoveOutlineHeading(transactions *[]*Transaction) bool {
  64. for _, tx := range *transactions {
  65. for _, op := range tx.DoOperations {
  66. if "moveOutlineHeading" == op.Action {
  67. return true
  68. }
  69. }
  70. }
  71. return false
  72. }
  73. func WaitForWritingFiles() {
  74. var printLog bool
  75. var lastPrintLog bool
  76. for i := 0; isWritingFiles(); i++ {
  77. time.Sleep(5 * time.Millisecond)
  78. if 2000 < i && !printLog { // 10s 后打日志
  79. logging.LogWarnf("file is writing: \n%s", logging.ShortStack())
  80. printLog = true
  81. }
  82. if 12000 < i && !lastPrintLog { // 60s 后打日志
  83. logging.LogWarnf("file is still writing")
  84. lastPrintLog = true
  85. }
  86. }
  87. }
  88. var (
  89. txQueue = make(chan *Transaction, 7)
  90. flushLock = sync.Mutex{}
  91. )
  92. func isWritingFiles() bool {
  93. time.Sleep(time.Duration(50) * time.Millisecond)
  94. return 0 < len(txQueue)
  95. }
  96. func init() {
  97. go func() {
  98. for {
  99. select {
  100. case tx := <-txQueue:
  101. flushTx(tx)
  102. }
  103. }
  104. }()
  105. }
  106. func flushTx(tx *Transaction) {
  107. defer logging.Recover()
  108. flushLock.Lock()
  109. defer flushLock.Unlock()
  110. start := time.Now()
  111. if txErr := performTx(tx); nil != txErr {
  112. switch txErr.code {
  113. case TxErrCodeBlockNotFound:
  114. util.PushTxErr("Transaction failed", txErr.code, nil)
  115. return
  116. case TxErrCodeDataIsSyncing:
  117. util.PushMsg(Conf.Language(222), 5000)
  118. default:
  119. txData, _ := gulu.JSON.MarshalJSON(tx)
  120. logging.LogFatalf(logging.ExitCodeFatal, "transaction failed [%d]: %s\n tx [%s]", txErr.code, txErr.msg, txData)
  121. }
  122. }
  123. elapsed := time.Now().Sub(start).Milliseconds()
  124. if 0 < len(tx.DoOperations) {
  125. if 2000 < elapsed {
  126. logging.LogWarnf("op tx [%dms]", elapsed)
  127. }
  128. }
  129. }
  130. func PerformTransactions(transactions *[]*Transaction) {
  131. for _, tx := range *transactions {
  132. tx.m = &sync.Mutex{}
  133. txQueue <- tx
  134. }
  135. return
  136. }
  137. const (
  138. TxErrCodeBlockNotFound = 0
  139. TxErrCodeDataIsSyncing = 1
  140. TxErrCodeWriteTree = 2
  141. TxErrWriteAttributeView = 3
  142. )
  143. type TxErr struct {
  144. code int
  145. msg string
  146. id string
  147. }
  148. func performTx(tx *Transaction) (ret *TxErr) {
  149. if 1 > len(tx.DoOperations) {
  150. return
  151. }
  152. //os.MkdirAll("pprof", 0755)
  153. //cpuProfile, _ := os.Create("pprof/cpu_profile_tx")
  154. //pprof.StartCPUProfile(cpuProfile)
  155. //defer pprof.StopCPUProfile()
  156. var err error
  157. if err = tx.begin(); nil != err {
  158. if strings.Contains(err.Error(), "database is closed") {
  159. return
  160. }
  161. logging.LogErrorf("begin tx failed: %s", err)
  162. ret = &TxErr{msg: err.Error()}
  163. return
  164. }
  165. defer func() {
  166. if e := recover(); nil != e {
  167. stack := debug.Stack()
  168. msg := fmt.Sprintf("PANIC RECOVERED: %v\n\t%s\n", e, stack)
  169. logging.LogErrorf(msg)
  170. if 1 == tx.state.Load() {
  171. tx.rollback()
  172. return
  173. }
  174. }
  175. }()
  176. for _, op := range tx.DoOperations {
  177. switch op.Action {
  178. case "create":
  179. ret = tx.doCreate(op)
  180. case "update":
  181. ret = tx.doUpdate(op)
  182. case "insert":
  183. ret = tx.doInsert(op)
  184. case "delete":
  185. ret = tx.doDelete(op)
  186. case "move":
  187. ret = tx.doMove(op)
  188. case "moveOutlineHeading":
  189. ret = tx.doMoveOutlineHeading(op)
  190. case "append":
  191. ret = tx.doAppend(op)
  192. case "appendInsert":
  193. ret = tx.doAppendInsert(op)
  194. case "prependInsert":
  195. ret = tx.doPrependInsert(op)
  196. case "foldHeading":
  197. ret = tx.doFoldHeading(op)
  198. case "unfoldHeading":
  199. ret = tx.doUnfoldHeading(op)
  200. case "setAttrs":
  201. ret = tx.doSetAttrs(op)
  202. case "doUpdateUpdated":
  203. ret = tx.doUpdateUpdated(op)
  204. case "addFlashcards":
  205. ret = tx.doAddFlashcards(op)
  206. case "removeFlashcards":
  207. ret = tx.doRemoveFlashcards(op)
  208. case "setAttrViewName":
  209. ret = tx.doSetAttrViewName(op)
  210. case "setAttrViewFilters":
  211. ret = tx.doSetAttrViewFilters(op)
  212. case "setAttrViewSorts":
  213. ret = tx.doSetAttrViewSorts(op)
  214. case "setAttrViewPageSize":
  215. ret = tx.doSetAttrViewPageSize(op)
  216. case "setAttrViewColWidth":
  217. ret = tx.doSetAttrViewColumnWidth(op)
  218. case "setAttrViewColWrap":
  219. ret = tx.doSetAttrViewColumnWrap(op)
  220. case "setAttrViewColHidden":
  221. ret = tx.doSetAttrViewColumnHidden(op)
  222. case "setAttrViewColPin":
  223. ret = tx.doSetAttrViewColumnPin(op)
  224. case "setAttrViewColIcon":
  225. ret = tx.doSetAttrViewColumnIcon(op)
  226. case "insertAttrViewBlock":
  227. ret = tx.doInsertAttrViewBlock(op)
  228. case "removeAttrViewBlock":
  229. ret = tx.doRemoveAttrViewBlock(op)
  230. case "addAttrViewCol":
  231. ret = tx.doAddAttrViewColumn(op)
  232. case "updateAttrViewCol":
  233. ret = tx.doUpdateAttrViewColumn(op)
  234. case "removeAttrViewCol":
  235. ret = tx.doRemoveAttrViewColumn(op)
  236. case "sortAttrViewRow":
  237. ret = tx.doSortAttrViewRow(op)
  238. case "sortAttrViewCol":
  239. ret = tx.doSortAttrViewColumn(op)
  240. case "sortAttrViewKey":
  241. ret = tx.doSortAttrViewKey(op)
  242. case "updateAttrViewCell":
  243. ret = tx.doUpdateAttrViewCell(op)
  244. case "updateAttrViewColOptions":
  245. ret = tx.doUpdateAttrViewColOptions(op)
  246. case "removeAttrViewColOption":
  247. ret = tx.doRemoveAttrViewColOption(op)
  248. case "updateAttrViewColOption":
  249. ret = tx.doUpdateAttrViewColOption(op)
  250. case "setAttrViewColCalc":
  251. ret = tx.doSetAttrViewColCalc(op)
  252. case "updateAttrViewColNumberFormat":
  253. ret = tx.doUpdateAttrViewColNumberFormat(op)
  254. case "replaceAttrViewBlock":
  255. ret = tx.doReplaceAttrViewBlock(op)
  256. case "updateAttrViewColTemplate":
  257. ret = tx.doUpdateAttrViewColTemplate(op)
  258. case "addAttrViewView":
  259. ret = tx.doAddAttrViewView(op)
  260. case "removeAttrViewView":
  261. ret = tx.doRemoveAttrViewView(op)
  262. case "setAttrViewViewName":
  263. ret = tx.doSetAttrViewViewName(op)
  264. case "setAttrViewViewIcon":
  265. ret = tx.doSetAttrViewViewIcon(op)
  266. case "duplicateAttrViewView":
  267. ret = tx.doDuplicateAttrViewView(op)
  268. case "sortAttrViewView":
  269. ret = tx.doSortAttrViewView(op)
  270. case "updateAttrViewColRelation":
  271. ret = tx.doUpdateAttrViewColRelation(op)
  272. case "updateAttrViewColRollup":
  273. ret = tx.doUpdateAttrViewColRollup(op)
  274. case "hideAttrViewName":
  275. ret = tx.doHideAttrViewName(op)
  276. case "setAttrViewColDate":
  277. ret = tx.doSetAttrViewColDate(op)
  278. case "unbindAttrViewBlock":
  279. ret = tx.doUnbindAttrViewBlock(op)
  280. case "duplicateAttrViewKey":
  281. ret = tx.doDuplicateAttrViewKey(op)
  282. }
  283. if nil != ret {
  284. tx.rollback()
  285. return
  286. }
  287. }
  288. if cr := tx.commit(); nil != cr {
  289. logging.LogErrorf("commit tx failed: %s", cr)
  290. return &TxErr{msg: cr.Error()}
  291. }
  292. return
  293. }
  294. func (tx *Transaction) doMove(operation *Operation) (ret *TxErr) {
  295. var err error
  296. id := operation.ID
  297. srcTree, err := tx.loadTree(id)
  298. if nil != err {
  299. logging.LogErrorf("load tree [%s] failed: %s", id, err)
  300. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  301. }
  302. srcNode := treenode.GetNodeInTree(srcTree, id)
  303. if nil == srcNode {
  304. logging.LogErrorf("get node [%s] in tree [%s] failed", id, srcTree.Root.ID)
  305. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  306. }
  307. var headingChildren []*ast.Node
  308. if isMovingFoldHeading := ast.NodeHeading == srcNode.Type && "1" == srcNode.IALAttr("fold"); isMovingFoldHeading {
  309. headingChildren = treenode.HeadingChildren(srcNode)
  310. // Blocks below other non-folded headings are no longer moved when moving a folded heading https://github.com/siyuan-note/siyuan/issues/8321
  311. headingChildren = treenode.GetHeadingFold(headingChildren)
  312. }
  313. refreshHeadingChildrenUpdated(srcNode, time.Now().Format("20060102150405"))
  314. var srcEmptyList *ast.Node
  315. if ast.NodeListItem == srcNode.Type && srcNode.Parent.FirstChild == srcNode && srcNode.Parent.LastChild == srcNode {
  316. // 列表中唯一的列表项被移除后,该列表就为空了
  317. srcEmptyList = srcNode.Parent
  318. }
  319. targetPreviousID := operation.PreviousID
  320. targetParentID := operation.ParentID
  321. if "" != targetPreviousID {
  322. if id == targetPreviousID {
  323. return
  324. }
  325. var targetTree *parse.Tree
  326. targetTree, err = tx.loadTree(targetPreviousID)
  327. if nil != err {
  328. logging.LogErrorf("load tree [%s] failed: %s", targetPreviousID, err)
  329. return &TxErr{code: TxErrCodeBlockNotFound, id: targetPreviousID}
  330. }
  331. isSameTree := srcTree.ID == targetTree.ID
  332. if isSameTree {
  333. targetTree = srcTree
  334. }
  335. targetNode := treenode.GetNodeInTree(targetTree, targetPreviousID)
  336. if nil == targetNode {
  337. logging.LogErrorf("get node [%s] in tree [%s] failed", targetPreviousID, targetTree.Root.ID)
  338. return &TxErr{code: TxErrCodeBlockNotFound, id: targetPreviousID}
  339. }
  340. if ast.NodeHeading == targetNode.Type && "1" == targetNode.IALAttr("fold") {
  341. targetChildren := treenode.HeadingChildren(targetNode)
  342. targetChildren = treenode.GetHeadingFold(targetChildren)
  343. if l := len(targetChildren); 0 < l {
  344. targetNode = targetChildren[l-1]
  345. }
  346. }
  347. if isMovingFoldHeadingIntoSelf(targetNode, headingChildren) {
  348. return
  349. }
  350. for i := len(headingChildren) - 1; -1 < i; i-- {
  351. c := headingChildren[i]
  352. targetNode.InsertAfter(c)
  353. }
  354. targetNode.InsertAfter(srcNode)
  355. if nil != srcEmptyList {
  356. srcEmptyList.Unlink()
  357. }
  358. refreshHeadingChildrenUpdated(srcNode, time.Now().Format("20060102150405"))
  359. refreshUpdated(srcNode)
  360. refreshUpdated(srcTree.Root)
  361. if err = tx.writeTree(srcTree); nil != err {
  362. return
  363. }
  364. if !isSameTree {
  365. if err = tx.writeTree(targetTree); nil != err {
  366. return
  367. }
  368. }
  369. return
  370. }
  371. if id == targetParentID {
  372. return
  373. }
  374. targetTree, err := tx.loadTree(targetParentID)
  375. if nil != err {
  376. logging.LogErrorf("load tree [%s] failed: %s", targetParentID, err)
  377. return &TxErr{code: TxErrCodeBlockNotFound, id: targetParentID}
  378. }
  379. isSameTree := srcTree.ID == targetTree.ID
  380. if isSameTree {
  381. targetTree = srcTree
  382. }
  383. targetNode := treenode.GetNodeInTree(targetTree, targetParentID)
  384. if nil == targetNode {
  385. logging.LogErrorf("get node [%s] in tree [%s] failed", targetParentID, targetTree.Root.ID)
  386. return &TxErr{code: TxErrCodeBlockNotFound, id: targetParentID}
  387. }
  388. if isMovingFoldHeadingIntoSelf(targetNode, headingChildren) {
  389. return
  390. }
  391. processed := false
  392. if ast.NodeSuperBlock == targetNode.Type {
  393. // 在布局节点后插入
  394. targetNode = targetNode.FirstChild.Next
  395. for i := len(headingChildren) - 1; -1 < i; i-- {
  396. c := headingChildren[i]
  397. targetNode.InsertAfter(c)
  398. }
  399. targetNode.InsertAfter(srcNode)
  400. if nil != srcEmptyList {
  401. srcEmptyList.Unlink()
  402. }
  403. processed = true
  404. } else if ast.NodeListItem == targetNode.Type {
  405. if 3 == targetNode.ListData.Typ {
  406. // 在任务列表标记节点后插入
  407. targetNode = targetNode.FirstChild
  408. for i := len(headingChildren) - 1; -1 < i; i-- {
  409. c := headingChildren[i]
  410. targetNode.InsertAfter(c)
  411. }
  412. targetNode.InsertAfter(srcNode)
  413. if nil != srcEmptyList {
  414. srcEmptyList.Unlink()
  415. }
  416. processed = true
  417. }
  418. }
  419. if !processed {
  420. for i := len(headingChildren) - 1; -1 < i; i-- {
  421. c := headingChildren[i]
  422. targetNode.PrependChild(c)
  423. }
  424. targetNode.PrependChild(srcNode)
  425. if nil != srcEmptyList {
  426. srcEmptyList.Unlink()
  427. }
  428. }
  429. refreshHeadingChildrenUpdated(srcNode, time.Now().Format("20060102150405"))
  430. refreshUpdated(srcNode)
  431. refreshUpdated(srcTree.Root)
  432. if err = tx.writeTree(srcTree); nil != err {
  433. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: id}
  434. }
  435. if !isSameTree {
  436. if err = tx.writeTree(targetTree); nil != err {
  437. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: id}
  438. }
  439. }
  440. return
  441. }
  442. func isMovingFoldHeadingIntoSelf(targetNode *ast.Node, headingChildren []*ast.Node) bool {
  443. for _, headingChild := range headingChildren {
  444. if headingChild.ID == targetNode.ID {
  445. // 不能将折叠标题移动到自己下方节点的前或后 https://github.com/siyuan-note/siyuan/issues/7163
  446. return true
  447. }
  448. }
  449. return false
  450. }
  451. func (tx *Transaction) doPrependInsert(operation *Operation) (ret *TxErr) {
  452. var err error
  453. block := treenode.GetBlockTree(operation.ParentID)
  454. if nil == block {
  455. logging.LogWarnf("not found block [%s]", operation.ParentID)
  456. util.ReloadUI() // 比如分屏后编辑器状态不一致,这里强制重新载入界面
  457. return
  458. }
  459. tree, err := tx.loadTree(block.ID)
  460. if nil != err {
  461. msg := fmt.Sprintf("load tree [%s] failed: %s", block.ID, err)
  462. logging.LogErrorf(msg)
  463. return &TxErr{code: TxErrCodeBlockNotFound, id: block.ID}
  464. }
  465. data := strings.ReplaceAll(operation.Data.(string), editor.FrontEndCaret, "")
  466. subTree := tx.luteEngine.BlockDOM2Tree(data)
  467. insertedNode := subTree.Root.FirstChild
  468. if nil == insertedNode {
  469. return &TxErr{code: TxErrCodeBlockNotFound, msg: "invalid data tree", id: block.ID}
  470. }
  471. var remains []*ast.Node
  472. for remain := insertedNode.Next; nil != remain; remain = remain.Next {
  473. if ast.NodeKramdownBlockIAL != remain.Type {
  474. if "" == remain.ID {
  475. remain.ID = ast.NewNodeID()
  476. remain.SetIALAttr("id", remain.ID)
  477. }
  478. remains = append(remains, remain)
  479. }
  480. }
  481. if "" == insertedNode.ID {
  482. insertedNode.ID = ast.NewNodeID()
  483. insertedNode.SetIALAttr("id", insertedNode.ID)
  484. }
  485. node := treenode.GetNodeInTree(tree, operation.ParentID)
  486. if nil == node {
  487. logging.LogErrorf("get node [%s] in tree [%s] failed", operation.ParentID, tree.Root.ID)
  488. return &TxErr{code: TxErrCodeBlockNotFound, id: operation.ParentID}
  489. }
  490. isContainer := node.IsContainerBlock()
  491. for i := len(remains) - 1; 0 <= i; i-- {
  492. remain := remains[i]
  493. if isContainer {
  494. if ast.NodeListItem == node.Type && 3 == node.ListData.Typ {
  495. node.FirstChild.InsertAfter(remain)
  496. } else if ast.NodeSuperBlock == node.Type {
  497. node.FirstChild.Next.InsertAfter(remain)
  498. } else {
  499. node.PrependChild(remain)
  500. }
  501. } else {
  502. node.InsertAfter(remain)
  503. }
  504. }
  505. if isContainer {
  506. if ast.NodeListItem == node.Type && 3 == node.ListData.Typ {
  507. node.FirstChild.InsertAfter(insertedNode)
  508. } else if ast.NodeSuperBlock == node.Type {
  509. node.FirstChild.Next.InsertAfter(insertedNode)
  510. } else {
  511. node.PrependChild(insertedNode)
  512. }
  513. } else {
  514. node.InsertAfter(insertedNode)
  515. }
  516. createdUpdated(insertedNode)
  517. tx.nodes[insertedNode.ID] = insertedNode
  518. if err = tx.writeTree(tree); nil != err {
  519. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: block.ID}
  520. }
  521. operation.ID = insertedNode.ID
  522. operation.ParentID = insertedNode.Parent.ID
  523. // 将 prependInsert 转换为 insert 推送
  524. operation.Action = "insert"
  525. if nil != insertedNode.Previous {
  526. operation.PreviousID = insertedNode.Previous.ID
  527. }
  528. return
  529. }
  530. func (tx *Transaction) doAppendInsert(operation *Operation) (ret *TxErr) {
  531. var err error
  532. block := treenode.GetBlockTree(operation.ParentID)
  533. if nil == block {
  534. logging.LogWarnf("not found block [%s]", operation.ParentID)
  535. util.ReloadUI() // 比如分屏后编辑器状态不一致,这里强制重新载入界面
  536. return
  537. }
  538. tree, err := tx.loadTree(block.ID)
  539. if nil != err {
  540. msg := fmt.Sprintf("load tree [%s] failed: %s", block.ID, err)
  541. logging.LogErrorf(msg)
  542. return &TxErr{code: TxErrCodeBlockNotFound, id: block.ID}
  543. }
  544. data := strings.ReplaceAll(operation.Data.(string), editor.FrontEndCaret, "")
  545. subTree := tx.luteEngine.BlockDOM2Tree(data)
  546. insertedNode := subTree.Root.FirstChild
  547. if nil == insertedNode {
  548. return &TxErr{code: TxErrCodeBlockNotFound, msg: "invalid data tree", id: block.ID}
  549. }
  550. if "" == insertedNode.ID {
  551. insertedNode.ID = ast.NewNodeID()
  552. insertedNode.SetIALAttr("id", insertedNode.ID)
  553. }
  554. var toInserts []*ast.Node
  555. for toInsert := insertedNode; nil != toInsert; toInsert = toInsert.Next {
  556. if ast.NodeKramdownBlockIAL != toInsert.Type {
  557. if "" == toInsert.ID {
  558. toInsert.ID = ast.NewNodeID()
  559. toInsert.SetIALAttr("id", toInsert.ID)
  560. }
  561. toInserts = append(toInserts, toInsert)
  562. }
  563. }
  564. node := treenode.GetNodeInTree(tree, operation.ParentID)
  565. if nil == node {
  566. logging.LogErrorf("get node [%s] in tree [%s] failed", operation.ParentID, tree.Root.ID)
  567. return &TxErr{code: TxErrCodeBlockNotFound, id: operation.ParentID}
  568. }
  569. isContainer := node.IsContainerBlock()
  570. for i := 0; i < len(toInserts); i++ {
  571. toInsert := toInserts[i]
  572. if isContainer {
  573. if ast.NodeList == node.Type {
  574. // 列表下只能挂列表项,所以这里需要分情况处理 https://github.com/siyuan-note/siyuan/issues/9955
  575. if ast.NodeList == toInsert.Type {
  576. var childLis []*ast.Node
  577. for childLi := toInsert.FirstChild; nil != childLi; childLi = childLi.Next {
  578. childLis = append(childLis, childLi)
  579. }
  580. for _, childLi := range childLis {
  581. node.AppendChild(childLi)
  582. }
  583. } else {
  584. newLiID := ast.NewNodeID()
  585. newLi := &ast.Node{ID: newLiID, Type: ast.NodeListItem, ListData: &ast.ListData{Typ: node.ListData.Typ}}
  586. newLi.SetIALAttr("id", newLiID)
  587. node.AppendChild(newLi)
  588. newLi.AppendChild(toInsert)
  589. }
  590. } else if ast.NodeSuperBlock == node.Type {
  591. node.LastChild.InsertBefore(toInsert)
  592. } else {
  593. node.AppendChild(toInsert)
  594. }
  595. } else {
  596. node.InsertAfter(toInsert)
  597. }
  598. }
  599. createdUpdated(insertedNode)
  600. tx.nodes[insertedNode.ID] = insertedNode
  601. if err = tx.writeTree(tree); nil != err {
  602. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: block.ID}
  603. }
  604. operation.ID = insertedNode.ID
  605. operation.ParentID = insertedNode.Parent.ID
  606. // 将 appendInsert 转换为 insert 推送
  607. operation.Action = "insert"
  608. if nil != insertedNode.Previous {
  609. operation.PreviousID = insertedNode.Previous.ID
  610. }
  611. return
  612. }
  613. func (tx *Transaction) doAppend(operation *Operation) (ret *TxErr) {
  614. var err error
  615. id := operation.ID
  616. srcTree, err := tx.loadTree(id)
  617. if nil != err {
  618. logging.LogErrorf("load tree [%s] failed: %s", id, err)
  619. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  620. }
  621. srcNode := treenode.GetNodeInTree(srcTree, id)
  622. if nil == srcNode {
  623. logging.LogErrorf("get node [%s] in tree [%s] failed", id, srcTree.Root.ID)
  624. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  625. }
  626. if ast.NodeDocument == srcNode.Type {
  627. logging.LogWarnf("can't append a root to another root")
  628. return
  629. }
  630. var headingChildren []*ast.Node
  631. if isMovingFoldHeading := ast.NodeHeading == srcNode.Type && "1" == srcNode.IALAttr("fold"); isMovingFoldHeading {
  632. headingChildren = treenode.HeadingChildren(srcNode)
  633. }
  634. var srcEmptyList, targetNewList *ast.Node
  635. if ast.NodeListItem == srcNode.Type {
  636. targetNewListID := ast.NewNodeID()
  637. targetNewList = &ast.Node{ID: targetNewListID, Type: ast.NodeList, ListData: &ast.ListData{Typ: srcNode.ListData.Typ}}
  638. targetNewList.SetIALAttr("id", targetNewListID)
  639. if srcNode.Parent.FirstChild == srcNode && srcNode.Parent.LastChild == srcNode {
  640. // 列表中唯一的列表项被移除后,该列表就为空了
  641. srcEmptyList = srcNode.Parent
  642. }
  643. }
  644. targetRootID := operation.ParentID
  645. if id == targetRootID {
  646. logging.LogWarnf("target root id is nil")
  647. return
  648. }
  649. targetTree, err := tx.loadTree(targetRootID)
  650. if nil != err {
  651. logging.LogErrorf("load tree [%s] failed: %s", targetRootID, err)
  652. return &TxErr{code: TxErrCodeBlockNotFound, id: targetRootID}
  653. }
  654. isSameTree := srcTree.ID == targetTree.ID
  655. if isSameTree {
  656. targetTree = srcTree
  657. }
  658. targetRoot := targetTree.Root
  659. if nil != targetNewList {
  660. if nil != targetRoot.LastChild {
  661. if ast.NodeList != targetRoot.LastChild.Type {
  662. targetNewList.AppendChild(srcNode)
  663. targetRoot.AppendChild(targetNewList)
  664. } else {
  665. targetRoot.LastChild.AppendChild(srcNode)
  666. }
  667. } else {
  668. targetRoot.AppendChild(srcNode)
  669. }
  670. } else {
  671. targetRoot.AppendChild(srcNode)
  672. }
  673. for _, c := range headingChildren {
  674. targetRoot.AppendChild(c)
  675. }
  676. if nil != srcEmptyList {
  677. srcEmptyList.Unlink()
  678. }
  679. if err = tx.writeTree(srcTree); nil != err {
  680. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: id}
  681. }
  682. if !isSameTree {
  683. if err = tx.writeTree(targetTree); nil != err {
  684. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: id}
  685. }
  686. }
  687. return
  688. }
  689. func (tx *Transaction) doDelete(operation *Operation) (ret *TxErr) {
  690. // logging.LogInfof("commit delete [%+v]", operation)
  691. var err error
  692. id := operation.ID
  693. tree, err := tx.loadTree(id)
  694. if nil != err {
  695. if errors.Is(err, ErrBlockNotFound) {
  696. // move 以后这里会空,算作正常情况
  697. return
  698. }
  699. msg := fmt.Sprintf("load tree [%s] failed: %s", id, err)
  700. logging.LogErrorf(msg)
  701. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  702. }
  703. node := treenode.GetNodeInTree(tree, id)
  704. if nil == node {
  705. return nil // move 以后的情况,列表项移动导致的状态异常 https://github.com/siyuan-note/insider/issues/961
  706. }
  707. parent := node.Parent
  708. if nil != node.Next && ast.NodeKramdownBlockIAL == node.Next.Type && bytes.Contains(node.Next.Tokens, []byte(node.ID)) {
  709. // 列表块撤销状态异常 https://github.com/siyuan-note/siyuan/issues/3985
  710. node.Next.Unlink()
  711. }
  712. refreshHeadingChildrenUpdated(node, time.Now().Format("20060102150405"))
  713. node.Unlink()
  714. if nil != parent && ast.NodeListItem == parent.Type && nil == parent.FirstChild {
  715. // 保持空列表项
  716. node.FirstChild = nil
  717. parent.AppendChild(node)
  718. }
  719. treenode.RemoveBlockTree(node.ID)
  720. delete(tx.nodes, node.ID)
  721. if err = tx.writeTree(tree); nil != err {
  722. return
  723. }
  724. syncDelete2AttributeView(node)
  725. removeAvBlockRel(node)
  726. return
  727. }
  728. func removeAvBlockRel(node *ast.Node) {
  729. var avIDs []string
  730. ast.Walk(node, func(n *ast.Node, entering bool) ast.WalkStatus {
  731. if !entering {
  732. return ast.WalkContinue
  733. }
  734. if ast.NodeAttributeView == n.Type {
  735. avID := n.AttributeViewID
  736. if changed := av.RemoveBlockRel(avID, n.ID, treenode.ExistBlockTree); changed {
  737. avIDs = append(avIDs, avID)
  738. }
  739. }
  740. return ast.WalkContinue
  741. })
  742. avIDs = gulu.Str.RemoveDuplicatedElem(avIDs)
  743. for _, avID := range avIDs {
  744. util.PushReloadAttrView(avID)
  745. }
  746. }
  747. func syncDelete2AttributeView(node *ast.Node) {
  748. changedAvIDs := hashset.New()
  749. ast.Walk(node, func(n *ast.Node, entering bool) ast.WalkStatus {
  750. if !entering || !n.IsBlock() {
  751. return ast.WalkContinue
  752. }
  753. avs := n.IALAttr(av.NodeAttrNameAvs)
  754. if "" == avs {
  755. return ast.WalkContinue
  756. }
  757. avIDs := strings.Split(avs, ",")
  758. for _, avID := range avIDs {
  759. attrView, parseErr := av.ParseAttributeView(avID)
  760. if nil != parseErr {
  761. continue
  762. }
  763. changedAv := false
  764. blockValues := attrView.GetBlockKeyValues()
  765. if nil == blockValues {
  766. continue
  767. }
  768. for i, blockValue := range blockValues.Values {
  769. if blockValue.Block.ID == n.ID {
  770. blockValues.Values = append(blockValues.Values[:i], blockValues.Values[i+1:]...)
  771. changedAv = true
  772. break
  773. }
  774. }
  775. if changedAv {
  776. av.SaveAttributeView(attrView)
  777. changedAvIDs.Add(avID)
  778. }
  779. }
  780. return ast.WalkContinue
  781. })
  782. for _, avID := range changedAvIDs.Values() {
  783. util.PushReloadAttrView(avID.(string))
  784. }
  785. }
  786. func (tx *Transaction) doInsert(operation *Operation) (ret *TxErr) {
  787. var err error
  788. opParentID := operation.ParentID
  789. block := treenode.GetBlockTree(opParentID)
  790. if nil == block {
  791. block = treenode.GetBlockTree(operation.PreviousID)
  792. if nil == block {
  793. block = treenode.GetBlockTree(operation.NextID)
  794. }
  795. }
  796. if nil == block {
  797. logging.LogWarnf("not found block [%s, %s, %s]", operation.ParentID, operation.PreviousID, operation.NextID)
  798. util.ReloadUI() // 比如分屏后编辑器状态不一致,这里强制重新载入界面
  799. return
  800. }
  801. tree, err := tx.loadTree(block.ID)
  802. if nil != err {
  803. msg := fmt.Sprintf("load tree [%s] failed: %s", block.ID, err)
  804. logging.LogErrorf(msg)
  805. return &TxErr{code: TxErrCodeBlockNotFound, id: block.ID}
  806. }
  807. data := strings.ReplaceAll(operation.Data.(string), editor.FrontEndCaret, "")
  808. subTree := tx.luteEngine.BlockDOM2Tree(data)
  809. p := block.Path
  810. assets := getAssetsDir(filepath.Join(util.DataDir, block.BoxID), filepath.Dir(filepath.Join(util.DataDir, block.BoxID, p)))
  811. isGlobalAssets := strings.HasPrefix(assets, filepath.Join(util.DataDir, "assets"))
  812. if !isGlobalAssets {
  813. // 本地资源文件需要移动到用户手动建立的 assets 下 https://github.com/siyuan-note/siyuan/issues/2410
  814. ast.Walk(subTree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
  815. if !entering {
  816. return ast.WalkContinue
  817. }
  818. if ast.NodeLinkDest == n.Type && bytes.HasPrefix(n.Tokens, []byte("assets/")) {
  819. assetP := gulu.Str.FromBytes(n.Tokens)
  820. assetPath, e := GetAssetAbsPath(assetP)
  821. if nil != e {
  822. logging.LogErrorf("get path of asset [%s] failed: %s", assetP, err)
  823. return ast.WalkContinue
  824. }
  825. if !strings.HasPrefix(assetPath, filepath.Join(util.DataDir, "assets")) {
  826. // 非全局 assets 则跳过
  827. return ast.WalkContinue
  828. }
  829. // 只有全局 assets 才移动到相对 assets
  830. targetP := filepath.Join(assets, filepath.Base(assetPath))
  831. if e = filelock.Rename(assetPath, targetP); nil != err {
  832. logging.LogErrorf("copy path of asset from [%s] to [%s] failed: %s", assetPath, targetP, err)
  833. return ast.WalkContinue
  834. }
  835. }
  836. return ast.WalkContinue
  837. })
  838. }
  839. insertedNode := subTree.Root.FirstChild
  840. if nil == insertedNode {
  841. return &TxErr{code: TxErrCodeBlockNotFound, msg: "invalid data tree", id: block.ID}
  842. }
  843. var remains []*ast.Node
  844. for remain := insertedNode.Next; nil != remain; remain = remain.Next {
  845. if ast.NodeKramdownBlockIAL != remain.Type {
  846. if "" == remain.ID {
  847. remain.ID = ast.NewNodeID()
  848. remain.SetIALAttr("id", remain.ID)
  849. }
  850. remains = append(remains, remain)
  851. }
  852. }
  853. if "" == insertedNode.ID {
  854. insertedNode.ID = ast.NewNodeID()
  855. insertedNode.SetIALAttr("id", insertedNode.ID)
  856. }
  857. var node *ast.Node
  858. nextID := operation.NextID
  859. previousID := operation.PreviousID
  860. if "" != nextID {
  861. node = treenode.GetNodeInTree(tree, nextID)
  862. if nil == node {
  863. logging.LogErrorf("get node [%s] in tree [%s] failed", nextID, tree.Root.ID)
  864. return &TxErr{code: TxErrCodeBlockNotFound, id: nextID}
  865. }
  866. if ast.NodeList == insertedNode.Type && nil != node.Parent && ast.NodeList == node.Parent.Type {
  867. insertedNode = insertedNode.FirstChild
  868. }
  869. node.InsertBefore(insertedNode)
  870. } else if "" != previousID {
  871. node = treenode.GetNodeInTree(tree, previousID)
  872. if nil == node {
  873. logging.LogErrorf("get node [%s] in tree [%s] failed", previousID, tree.Root.ID)
  874. return &TxErr{code: TxErrCodeBlockNotFound, id: previousID}
  875. }
  876. if ast.NodeHeading == node.Type && "1" == node.IALAttr("fold") {
  877. children := treenode.HeadingChildren(node)
  878. if l := len(children); 0 < l {
  879. node = children[l-1]
  880. }
  881. }
  882. if ast.NodeList == insertedNode.Type && nil != node.Parent && ast.NodeList == node.Parent.Type {
  883. insertedNode = insertedNode.FirstChild
  884. }
  885. for i := len(remains) - 1; 0 <= i; i-- {
  886. remain := remains[i]
  887. node.InsertAfter(remain)
  888. }
  889. node.InsertAfter(insertedNode)
  890. } else {
  891. node = treenode.GetNodeInTree(tree, operation.ParentID)
  892. if nil == node {
  893. logging.LogErrorf("get node [%s] in tree [%s] failed", operation.ParentID, tree.Root.ID)
  894. return &TxErr{code: TxErrCodeBlockNotFound, id: operation.ParentID}
  895. }
  896. if ast.NodeSuperBlock == node.Type {
  897. // 在布局节点后插入
  898. node.FirstChild.Next.InsertAfter(insertedNode)
  899. } else {
  900. if ast.NodeList == insertedNode.Type && nil != insertedNode.FirstChild && operation.ID == insertedNode.FirstChild.ID && operation.ID != insertedNode.ID {
  901. // 将一个列表项移动到另一个列表的第一项时 https://github.com/siyuan-note/siyuan/issues/2341
  902. insertedNode = insertedNode.FirstChild
  903. }
  904. if ast.NodeListItem == node.Type && 3 == node.ListData.Typ {
  905. // 在任务列表标记节点后插入
  906. node.FirstChild.InsertAfter(insertedNode)
  907. for _, remain := range remains {
  908. node.FirstChild.InsertAfter(remain)
  909. }
  910. } else {
  911. for i := len(remains) - 1; 0 <= i; i-- {
  912. remain := remains[i]
  913. node.PrependChild(remain)
  914. }
  915. node.PrependChild(insertedNode)
  916. }
  917. }
  918. }
  919. refreshHeadingChildrenUpdated(insertedNode, time.Now().Format("20060102150405"))
  920. createdUpdated(insertedNode)
  921. tx.nodes[insertedNode.ID] = insertedNode
  922. if err = tx.writeTree(tree); nil != err {
  923. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: block.ID}
  924. }
  925. upsertAvBlockRel(insertedNode)
  926. operation.ID = insertedNode.ID
  927. operation.ParentID = insertedNode.Parent.ID
  928. checkUpsertInUserGuide(tree)
  929. return
  930. }
  931. func (tx *Transaction) doUpdate(operation *Operation) (ret *TxErr) {
  932. id := operation.ID
  933. tree, err := tx.loadTree(id)
  934. if nil != err {
  935. if errors.Is(err, ErrBlockNotFound) {
  936. logging.LogWarnf("not found block [%s]", id)
  937. return
  938. }
  939. logging.LogErrorf("load tree [%s] failed: %s", id, err)
  940. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  941. }
  942. data := strings.ReplaceAll(operation.Data.(string), editor.FrontEndCaret, "")
  943. if "" == data {
  944. logging.LogErrorf("update data is nil")
  945. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  946. }
  947. subTree := tx.luteEngine.BlockDOM2Tree(data)
  948. subTree.ID, subTree.Box, subTree.Path = tree.ID, tree.Box, tree.Path
  949. oldNode := treenode.GetNodeInTree(tree, id)
  950. if nil == oldNode {
  951. logging.LogErrorf("get node [%s] in tree [%s] failed", id, tree.Root.ID)
  952. return &TxErr{msg: ErrBlockNotFound.Error(), id: id}
  953. }
  954. var unlinks []*ast.Node
  955. ast.Walk(subTree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
  956. if !entering {
  957. return ast.WalkContinue
  958. }
  959. if ast.NodeTextMark == n.Type {
  960. if n.IsTextMarkType("inline-math") {
  961. if "" == strings.TrimSpace(n.TextMarkInlineMathContent) {
  962. // 剔除空白的行级公式
  963. unlinks = append(unlinks, n)
  964. }
  965. } else if n.IsTextMarkType("block-ref") {
  966. sql.CacheRef(subTree, n)
  967. if "d" == n.TextMarkBlockRefSubtype {
  968. // 偶发编辑文档标题后引用处的动态锚文本不更新 https://github.com/siyuan-note/siyuan/issues/5891
  969. // 使用缓存的动态锚文本强制覆盖当前块中的引用节点动态锚文本
  970. if dRefText, ok := treenode.DynamicRefTexts.Load(n.TextMarkBlockRefID); ok && "" != dRefText {
  971. n.TextMarkTextContent = dRefText.(string)
  972. }
  973. }
  974. }
  975. }
  976. return ast.WalkContinue
  977. })
  978. for _, n := range unlinks {
  979. n.Unlink()
  980. }
  981. updatedNode := subTree.Root.FirstChild
  982. if nil == updatedNode {
  983. logging.LogErrorf("get fist node in sub tree [%s] failed", subTree.Root.ID)
  984. return &TxErr{msg: ErrBlockNotFound.Error(), id: id}
  985. }
  986. if ast.NodeList == updatedNode.Type && ast.NodeList == oldNode.Parent.Type {
  987. updatedNode = updatedNode.FirstChild
  988. }
  989. if oldNode.IsContainerBlock() {
  990. // 更新容器块的话需要考虑其子块中可能存在的折叠标题,需要把这些折叠标题的下方块移动到新节点下面
  991. treenode.MoveFoldHeading(updatedNode, oldNode)
  992. }
  993. refreshHeadingChildrenUpdated(oldNode, time.Now().Format("20060102150405"))
  994. cache.PutBlockIAL(updatedNode.ID, parse.IAL2Map(updatedNode.KramdownIAL))
  995. // 替换为新节点
  996. oldNode.InsertAfter(updatedNode)
  997. oldNode.Unlink()
  998. createdUpdated(updatedNode)
  999. refreshHeadingChildrenUpdated(updatedNode, updatedNode.IALAttr("updated"))
  1000. tx.nodes[updatedNode.ID] = updatedNode
  1001. if err = tx.writeTree(tree); nil != err {
  1002. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: id}
  1003. }
  1004. upsertAvBlockRel(updatedNode)
  1005. checkUpsertInUserGuide(tree)
  1006. return
  1007. }
  1008. func refreshHeadingChildrenUpdated(heading *ast.Node, updated string) {
  1009. if nil == heading || ast.NodeHeading != heading.Type {
  1010. return
  1011. }
  1012. // 将非标题块更新为标题块时需要更新下方块的 parent id
  1013. // The parent block field of the blocks under the heading block is calculated incorrectly https://github.com/siyuan-note/siyuan/issues/9869
  1014. children := treenode.HeadingChildren(heading)
  1015. for _, child := range children {
  1016. child.SetIALAttr("updated", updated)
  1017. }
  1018. }
  1019. func upsertAvBlockRel(node *ast.Node) {
  1020. var avIDs []string
  1021. ast.Walk(node, func(n *ast.Node, entering bool) ast.WalkStatus {
  1022. if !entering {
  1023. return ast.WalkContinue
  1024. }
  1025. if ast.NodeAttributeView == n.Type {
  1026. avID := n.AttributeViewID
  1027. if changed := av.UpsertBlockRel(avID, n.ID); changed {
  1028. avIDs = append(avIDs, avID)
  1029. }
  1030. }
  1031. return ast.WalkContinue
  1032. })
  1033. avIDs = gulu.Str.RemoveDuplicatedElem(avIDs)
  1034. for _, avID := range avIDs {
  1035. util.PushReloadAttrView(avID)
  1036. }
  1037. }
  1038. func (tx *Transaction) doUpdateUpdated(operation *Operation) (ret *TxErr) {
  1039. id := operation.ID
  1040. tree, err := tx.loadTree(id)
  1041. if nil != err {
  1042. if errors.Is(err, ErrBlockNotFound) {
  1043. logging.LogWarnf("not found block [%s]", id)
  1044. return
  1045. }
  1046. logging.LogErrorf("load tree [%s] failed: %s", id, err)
  1047. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  1048. }
  1049. node := treenode.GetNodeInTree(tree, id)
  1050. if nil == node {
  1051. logging.LogErrorf("get node [%s] in tree [%s] failed", id, tree.Root.ID)
  1052. return &TxErr{msg: ErrBlockNotFound.Error(), id: id}
  1053. }
  1054. node.SetIALAttr("updated", operation.Data.(string))
  1055. createdUpdated(node)
  1056. tx.nodes[node.ID] = node
  1057. if err = tx.writeTree(tree); nil != err {
  1058. return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: id}
  1059. }
  1060. return
  1061. }
  1062. func (tx *Transaction) doCreate(operation *Operation) (ret *TxErr) {
  1063. tree := operation.Data.(*parse.Tree)
  1064. tx.writeTree(tree)
  1065. checkUpsertInUserGuide(tree)
  1066. return
  1067. }
  1068. func (tx *Transaction) doSetAttrs(operation *Operation) (ret *TxErr) {
  1069. id := operation.ID
  1070. tree, err := tx.loadTree(id)
  1071. if nil != err {
  1072. logging.LogErrorf("load tree [%s] failed: %s", id, err)
  1073. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  1074. }
  1075. node := treenode.GetNodeInTree(tree, id)
  1076. if nil == node {
  1077. logging.LogErrorf("get node [%s] in tree [%s] failed", id, tree.Root.ID)
  1078. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  1079. }
  1080. attrs := map[string]string{}
  1081. if err = gulu.JSON.UnmarshalJSON([]byte(operation.Data.(string)), &attrs); nil != err {
  1082. logging.LogErrorf("unmarshal attrs failed: %s", err)
  1083. return &TxErr{code: TxErrCodeBlockNotFound, id: id}
  1084. }
  1085. var invalidNames []string
  1086. for name := range attrs {
  1087. for i := 0; i < len(name); i++ {
  1088. if !lex.IsASCIILetterNumHyphen(name[i]) {
  1089. logging.LogWarnf("invalid attr name [%s]", name)
  1090. invalidNames = append(invalidNames, name)
  1091. }
  1092. }
  1093. }
  1094. for _, name := range invalidNames {
  1095. delete(attrs, name)
  1096. }
  1097. for name, value := range attrs {
  1098. if "" == value {
  1099. node.RemoveIALAttr(name)
  1100. } else {
  1101. node.SetIALAttr(name, value)
  1102. }
  1103. }
  1104. if err = tx.writeTree(tree); nil != err {
  1105. return
  1106. }
  1107. cache.PutBlockIAL(id, parse.IAL2Map(node.KramdownIAL))
  1108. return
  1109. }
  1110. func refreshUpdated(node *ast.Node) {
  1111. updated := util.CurrentTimeSecondsStr()
  1112. node.SetIALAttr("updated", updated)
  1113. parents := treenode.ParentNodesWithHeadings(node)
  1114. for _, parent := range parents { // 更新所有父节点的更新时间字段
  1115. parent.SetIALAttr("updated", updated)
  1116. }
  1117. }
  1118. func createdUpdated(node *ast.Node) {
  1119. created := util.TimeFromID(node.ID)
  1120. updated := node.IALAttr("updated")
  1121. if "" == updated {
  1122. updated = created
  1123. }
  1124. if updated < created {
  1125. updated = created // 复制粘贴块后创建时间小于更新时间 https://github.com/siyuan-note/siyuan/issues/3624
  1126. }
  1127. parents := treenode.ParentNodesWithHeadings(node)
  1128. for _, parent := range parents { // 更新所有父节点的更新时间字段
  1129. parent.SetIALAttr("updated", updated)
  1130. cache.PutBlockIAL(parent.ID, parse.IAL2Map(parent.KramdownIAL))
  1131. }
  1132. }
  1133. type Operation struct {
  1134. Action string `json:"action"`
  1135. Data interface{} `json:"data"`
  1136. ID string `json:"id"`
  1137. ParentID string `json:"parentID"`
  1138. PreviousID string `json:"previousID"`
  1139. NextID string `json:"nextID"`
  1140. RetData interface{} `json:"retData"`
  1141. BlockIDs []string `json:"blockIDs"`
  1142. BlockID string `json:"blockID"`
  1143. DeckID string `json:"deckID"` // 用于添加/删除闪卡
  1144. AvID string `json:"avID"` // 属性视图 ID
  1145. SrcIDs []string `json:"srcIDs"` // 用于从属性视图中删除行
  1146. Srcs []map[string]interface{} `json:"srcs"` // 用于添加属性视图行(包括绑定块){id, content, isDetached}
  1147. IsDetached bool `json:"isDetached"` // 用于标识是否未绑定块,仅存在于属性视图中
  1148. IgnoreFillFilterVal bool `json:"ignoreFillFilter"` // 用于标识是否忽略填充筛选值
  1149. Name string `json:"name"` // 属性视图列名
  1150. Typ string `json:"type"` // 属性视图列类型
  1151. Format string `json:"format"` // 属性视图列格式化
  1152. KeyID string `json:"keyID"` // 属性视列 ID
  1153. RowID string `json:"rowID"` // 属性视图行 ID
  1154. IsTwoWay bool `json:"isTwoWay"` // 属性视图关联列是否是双向关系
  1155. BackRelationKeyID string `json:"backRelationKeyID"` // 属性视图关联列回链关联列的 ID
  1156. }
  1157. type Transaction struct {
  1158. Timestamp int64 `json:"timestamp"`
  1159. DoOperations []*Operation `json:"doOperations"`
  1160. UndoOperations []*Operation `json:"undoOperations"`
  1161. trees map[string]*parse.Tree
  1162. nodes map[string]*ast.Node
  1163. luteEngine *lute.Lute
  1164. m *sync.Mutex
  1165. state atomic.Int32 // 0: 初始化,1:未提交,:2: 已提交,3: 已回滚
  1166. }
  1167. func (tx *Transaction) WaitForCommit() {
  1168. for {
  1169. if 1 == tx.state.Load() {
  1170. time.Sleep(10 * time.Millisecond)
  1171. continue
  1172. }
  1173. return
  1174. }
  1175. }
  1176. func (tx *Transaction) begin() (err error) {
  1177. if nil != err {
  1178. return
  1179. }
  1180. tx.trees = map[string]*parse.Tree{}
  1181. tx.nodes = map[string]*ast.Node{}
  1182. tx.luteEngine = util.NewLute()
  1183. tx.m.Lock()
  1184. tx.state.Store(1)
  1185. return
  1186. }
  1187. func (tx *Transaction) commit() (err error) {
  1188. for _, tree := range tx.trees {
  1189. if err = writeTreeUpsertQueue(tree); nil != err {
  1190. return
  1191. }
  1192. var sources []interface{}
  1193. sources = append(sources, tx)
  1194. util.PushSaveDoc(tree.ID, "tx", sources)
  1195. }
  1196. refreshDynamicRefTexts(tx.nodes, tx.trees)
  1197. IncSync()
  1198. tx.state.Store(2)
  1199. tx.m.Unlock()
  1200. return
  1201. }
  1202. func (tx *Transaction) rollback() {
  1203. tx.trees, tx.nodes = nil, nil
  1204. tx.state.Store(3)
  1205. tx.m.Unlock()
  1206. return
  1207. }
  1208. func (tx *Transaction) loadTree(id string) (ret *parse.Tree, err error) {
  1209. var rootID, box, p string
  1210. bt := treenode.GetBlockTree(id)
  1211. if nil == bt {
  1212. return nil, ErrBlockNotFound
  1213. }
  1214. rootID = bt.RootID
  1215. box = bt.BoxID
  1216. p = bt.Path
  1217. ret = tx.trees[rootID]
  1218. if nil != ret {
  1219. return
  1220. }
  1221. ret, err = filesys.LoadTree(box, p, tx.luteEngine)
  1222. if nil != err {
  1223. return
  1224. }
  1225. tx.trees[rootID] = ret
  1226. return
  1227. }
  1228. func (tx *Transaction) writeTree(tree *parse.Tree) (err error) {
  1229. tx.trees[tree.ID] = tree
  1230. treenode.IndexBlockTree(tree)
  1231. return
  1232. }
  1233. // refreshDynamicRefText 用于刷新块引用的动态锚文本。
  1234. // 该实现依赖了数据库缓存,导致外部调用时可能需要阻塞等待数据库写入后才能获取到 refs
  1235. func refreshDynamicRefText(updatedDefNode *ast.Node, updatedTree *parse.Tree) {
  1236. changedDefs := map[string]*ast.Node{updatedDefNode.ID: updatedDefNode}
  1237. changedTrees := map[string]*parse.Tree{updatedTree.ID: updatedTree}
  1238. refreshDynamicRefTexts(changedDefs, changedTrees)
  1239. }
  1240. // refreshDynamicRefTexts 用于批量刷新块引用的动态锚文本。
  1241. // 该实现依赖了数据库缓存,导致外部调用时可能需要阻塞等待数据库写入后才能获取到 refs
  1242. func refreshDynamicRefTexts(updatedDefNodes map[string]*ast.Node, updatedTrees map[string]*parse.Tree) {
  1243. // 1. 更新引用的动态锚文本
  1244. treeRefNodeIDs := map[string]*hashset.Set{}
  1245. for _, updateNode := range updatedDefNodes {
  1246. refs := sql.GetRefsCacheByDefID(updateNode.ID)
  1247. if nil != updateNode.Parent && ast.NodeDocument != updateNode.Parent.Type &&
  1248. updateNode.Parent.IsContainerBlock() && (updateNode == treenode.FirstLeafBlock(updateNode.Parent)) { // 容器块下第一个子块
  1249. var parentRefs []*sql.Ref
  1250. if ast.NodeListItem == updateNode.Parent.Type { // 引用列表块时动态锚文本未跟随定义块内容变动 https://github.com/siyuan-note/siyuan/issues/4393
  1251. parentRefs = sql.GetRefsCacheByDefID(updateNode.Parent.Parent.ID)
  1252. updatedDefNodes[updateNode.Parent.ID] = updateNode.Parent
  1253. updatedDefNodes[updateNode.Parent.Parent.ID] = updateNode.Parent.Parent
  1254. } else {
  1255. parentRefs = sql.GetRefsCacheByDefID(updateNode.Parent.ID)
  1256. updatedDefNodes[updateNode.Parent.ID] = updateNode.Parent
  1257. }
  1258. if 0 < len(parentRefs) {
  1259. refs = append(refs, parentRefs...)
  1260. }
  1261. }
  1262. for _, ref := range refs {
  1263. if refIDs, ok := treeRefNodeIDs[ref.RootID]; !ok {
  1264. refIDs = hashset.New()
  1265. refIDs.Add(ref.BlockID)
  1266. treeRefNodeIDs[ref.RootID] = refIDs
  1267. } else {
  1268. refIDs.Add(ref.BlockID)
  1269. }
  1270. }
  1271. }
  1272. changedRefTree := map[string]*parse.Tree{}
  1273. for refTreeID, refNodeIDs := range treeRefNodeIDs {
  1274. refTree, ok := updatedTrees[refTreeID]
  1275. if !ok {
  1276. var err error
  1277. refTree, err = LoadTreeByBlockID(refTreeID)
  1278. if nil != err {
  1279. continue
  1280. }
  1281. }
  1282. var refTreeChanged bool
  1283. ast.Walk(refTree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
  1284. if !entering {
  1285. return ast.WalkContinue
  1286. }
  1287. if n.IsBlock() && refNodeIDs.Contains(n.ID) {
  1288. changed := updateRefText(n, updatedDefNodes)
  1289. if !refTreeChanged && changed {
  1290. refTreeChanged = true
  1291. }
  1292. return ast.WalkContinue
  1293. }
  1294. return ast.WalkContinue
  1295. })
  1296. if refTreeChanged {
  1297. changedRefTree[refTreeID] = refTree
  1298. }
  1299. }
  1300. // 2. 更新属性视图主键内容
  1301. for _, updatedDefNode := range updatedDefNodes {
  1302. avs := updatedDefNode.IALAttr(av.NodeAttrNameAvs)
  1303. if "" == avs {
  1304. continue
  1305. }
  1306. avIDs := strings.Split(avs, ",")
  1307. for _, avID := range avIDs {
  1308. attrView, parseErr := av.ParseAttributeView(avID)
  1309. if nil != parseErr {
  1310. continue
  1311. }
  1312. changedAv := false
  1313. blockValues := attrView.GetBlockKeyValues()
  1314. if nil == blockValues {
  1315. continue
  1316. }
  1317. for _, blockValue := range blockValues.Values {
  1318. if blockValue.Block.ID == updatedDefNode.ID {
  1319. newContent := getNodeRefText(updatedDefNode)
  1320. if newContent != blockValue.Block.Content {
  1321. blockValue.Block.Content = newContent
  1322. changedAv = true
  1323. }
  1324. break
  1325. }
  1326. }
  1327. if changedAv {
  1328. av.SaveAttributeView(attrView)
  1329. util.PushReloadAttrView(avID)
  1330. }
  1331. }
  1332. }
  1333. // 3. 保存变更
  1334. for _, tree := range changedRefTree {
  1335. indexWriteTreeUpsertQueue(tree)
  1336. }
  1337. }
  1338. var updateRefTextRenameDocs = map[string]*parse.Tree{}
  1339. var updateRefTextRenameDocLock = sync.Mutex{}
  1340. func updateRefTextRenameDoc(renamedTree *parse.Tree) {
  1341. updateRefTextRenameDocLock.Lock()
  1342. updateRefTextRenameDocs[renamedTree.ID] = renamedTree
  1343. updateRefTextRenameDocLock.Unlock()
  1344. }
  1345. func FlushUpdateRefTextRenameDocJob() {
  1346. sql.WaitForWritingDatabase()
  1347. flushUpdateRefTextRenameDoc()
  1348. }
  1349. func flushUpdateRefTextRenameDoc() {
  1350. updateRefTextRenameDocLock.Lock()
  1351. defer updateRefTextRenameDocLock.Unlock()
  1352. for _, tree := range updateRefTextRenameDocs {
  1353. refreshDynamicRefText(tree.Root, tree)
  1354. }
  1355. updateRefTextRenameDocs = map[string]*parse.Tree{}
  1356. }
  1357. func updateRefText(refNode *ast.Node, changedDefNodes map[string]*ast.Node) (changed bool) {
  1358. ast.Walk(refNode, func(n *ast.Node, entering bool) ast.WalkStatus {
  1359. if !entering {
  1360. return ast.WalkContinue
  1361. }
  1362. if !treenode.IsBlockRef(n) {
  1363. return ast.WalkContinue
  1364. }
  1365. defID, _, subtype := treenode.GetBlockRef(n)
  1366. if "s" == subtype || "" == defID {
  1367. return ast.WalkContinue
  1368. }
  1369. defNode := changedDefNodes[defID]
  1370. if nil == defNode {
  1371. return ast.WalkSkipChildren
  1372. }
  1373. refText := getNodeRefText(defNode)
  1374. treenode.SetDynamicBlockRefText(n, refText)
  1375. changed = true
  1376. return ast.WalkContinue
  1377. })
  1378. return
  1379. }
  1380. func checkUpsertInUserGuide(tree *parse.Tree) {
  1381. // In production mode, data reset warning pops up when editing data in the user guide https://github.com/siyuan-note/siyuan/issues/9757
  1382. if "prod" == util.Mode && IsUserGuide(tree.Box) {
  1383. util.PushErrMsg(Conf.Language(52), 7000)
  1384. }
  1385. }