block.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  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. "errors"
  19. "fmt"
  20. "strconv"
  21. "time"
  22. "github.com/88250/lute/ast"
  23. "github.com/88250/lute/parse"
  24. "github.com/siyuan-note/siyuan/kernel/sql"
  25. "github.com/siyuan-note/siyuan/kernel/treenode"
  26. "github.com/siyuan-note/siyuan/kernel/util"
  27. )
  28. // Block 描述了内容块。
  29. type Block struct {
  30. Box string `json:"box"`
  31. Path string `json:"path"`
  32. HPath string `json:"hPath"`
  33. ID string `json:"id"`
  34. RootID string `json:"rootID"`
  35. ParentID string `json:"parentID"`
  36. Name string `json:"name"`
  37. Alias string `json:"alias"`
  38. Memo string `json:"memo"`
  39. Tag string `json:"tag"`
  40. Content string `json:"content"`
  41. FContent string `json:"fcontent"`
  42. Markdown string `json:"markdown"`
  43. Folded bool `json:"folded"`
  44. Type string `json:"type"`
  45. SubType string `json:"subType"`
  46. RefText string `json:"refText"`
  47. Defs []*Block `json:"-"` // 当前块引用了这些块,避免序列化 JSON 时产生循环引用
  48. Refs []*Block `json:"refs"` // 当前块被这些块引用
  49. DefID string `json:"defID"`
  50. DefPath string `json:"defPath"`
  51. IAL map[string]string `json:"ial"`
  52. Children []*Block `json:"children"`
  53. Depth int `json:"depth"`
  54. Count int `json:"count"`
  55. Sort int `json:"sort"`
  56. Created string `json:"created"`
  57. Updated string `json:"updated"`
  58. RiffCardID string `json:"riffCardID"`
  59. RiffCardReps uint64 `json:"riffCardReps"`
  60. }
  61. func (block *Block) IsContainerBlock() bool {
  62. switch block.Type {
  63. case "NodeDocument", "NodeBlockquote", "NodeList", "NodeListItem", "NodeSuperBlock":
  64. return true
  65. }
  66. return false
  67. }
  68. type Path struct {
  69. ID string `json:"id"` // 块 ID
  70. Box string `json:"box"` // 块 Box
  71. Name string `json:"name"` // 当前路径
  72. HPath string `json:"hPath"` // 人类可读路径
  73. Type string `json:"type"` // "path"
  74. NodeType string `json:"nodeType"` // 节点类型
  75. SubType string `json:"subType"` // 节点子类型
  76. Blocks []*Block `json:"blocks,omitempty"` // 子块节点
  77. Children []*Path `json:"children,omitempty"` // 子路径节点
  78. Depth int `json:"depth"` // 层级深度
  79. Count int `json:"count"` // 子块计数
  80. Updated string `json:"updated"` // 更新时间
  81. Created string `json:"created"` // 创建时间
  82. }
  83. func IsBlockFolded(id string) bool {
  84. for i := 0; i < 32; i++ {
  85. b, _ := getBlock(id, nil)
  86. if nil == b {
  87. return false
  88. }
  89. if "1" == b.IAL["fold"] {
  90. return true
  91. }
  92. id = b.ParentID
  93. }
  94. return false
  95. }
  96. func RecentUpdatedBlocks() (ret []*Block) {
  97. ret = []*Block{}
  98. sqlBlocks := sql.QueryRecentUpdatedBlocks()
  99. if 1 > len(sqlBlocks) {
  100. return
  101. }
  102. ret = fromSQLBlocks(&sqlBlocks, "", 0)
  103. return
  104. }
  105. func TransferBlockRef(fromID, toID string, refIDs []string) (err error) {
  106. toTree, _ := loadTreeByBlockID(toID)
  107. if nil == toTree {
  108. err = ErrBlockNotFound
  109. return
  110. }
  111. toNode := treenode.GetNodeInTree(toTree, toID)
  112. if nil == toNode {
  113. err = ErrBlockNotFound
  114. return
  115. }
  116. toRefText := getNodeRefText(toNode)
  117. util.PushMsg(Conf.Language(116), 7000)
  118. if 1 > len(refIDs) { // 如果不指定 refIDs,则转移所有引用了 fromID 的块
  119. refIDs, _ = sql.QueryRefIDsByDefID(fromID, false)
  120. }
  121. for _, refID := range refIDs {
  122. tree, _ := loadTreeByBlockID(refID)
  123. if nil == tree {
  124. continue
  125. }
  126. node := treenode.GetNodeInTree(tree, refID)
  127. textMarks := node.ChildrenByType(ast.NodeTextMark)
  128. for _, textMark := range textMarks {
  129. if textMark.IsTextMarkType("block-ref") && textMark.TextMarkBlockRefID == fromID {
  130. textMark.TextMarkBlockRefID = toID
  131. if "d" == textMark.TextMarkBlockRefSubtype {
  132. textMark.TextMarkTextContent = toRefText
  133. }
  134. }
  135. }
  136. if err = indexWriteJSONQueue(tree); nil != err {
  137. return
  138. }
  139. }
  140. sql.WaitForWritingDatabase()
  141. util.ReloadUI()
  142. return
  143. }
  144. func SwapBlockRef(refID, defID string, includeChildren bool) (err error) {
  145. refTree, err := loadTreeByBlockID(refID)
  146. if nil != err {
  147. return
  148. }
  149. refNode := treenode.GetNodeInTree(refTree, refID)
  150. if nil == refNode {
  151. return
  152. }
  153. if ast.NodeListItem == refNode.Parent.Type {
  154. refNode = refNode.Parent
  155. }
  156. defTree, err := loadTreeByBlockID(defID)
  157. if nil != err {
  158. return
  159. }
  160. sameTree := defTree.ID == refTree.ID
  161. var defNode *ast.Node
  162. if !sameTree {
  163. defNode = treenode.GetNodeInTree(defTree, defID)
  164. } else {
  165. defNode = treenode.GetNodeInTree(refTree, defID)
  166. }
  167. if nil == defNode {
  168. return
  169. }
  170. var defNodeChildren []*ast.Node
  171. if ast.NodeListItem == defNode.Parent.Type {
  172. defNode = defNode.Parent
  173. } else if ast.NodeHeading == defNode.Type && includeChildren {
  174. defNodeChildren = treenode.HeadingChildren(defNode)
  175. }
  176. if ast.NodeListItem == defNode.Type {
  177. for c := defNode.FirstChild; nil != c; c = c.Next {
  178. if ast.NodeList == c.Type {
  179. defNodeChildren = append(defNodeChildren, c)
  180. }
  181. }
  182. }
  183. refreshUpdated(defNode)
  184. refreshUpdated(refNode)
  185. refPivot := treenode.NewParagraph()
  186. refNode.InsertBefore(refPivot)
  187. if ast.NodeListItem == defNode.Type {
  188. if ast.NodeListItem == refNode.Type {
  189. if !includeChildren {
  190. for _, c := range defNodeChildren {
  191. refNode.AppendChild(c)
  192. }
  193. }
  194. defNode.InsertAfter(refNode)
  195. refPivot.InsertAfter(defNode)
  196. } else {
  197. newID := ast.NewNodeID()
  198. li := &ast.Node{ID: newID, Type: ast.NodeListItem, ListData: &ast.ListData{Typ: defNode.Parent.ListData.Typ}}
  199. li.SetIALAttr("id", newID)
  200. li.SetIALAttr("updated", newID[:14])
  201. li.AppendChild(refNode)
  202. defNode.InsertAfter(li)
  203. if !includeChildren {
  204. for _, c := range defNodeChildren {
  205. li.AppendChild(c)
  206. }
  207. }
  208. newID = ast.NewNodeID()
  209. list := &ast.Node{ID: newID, Type: ast.NodeList, ListData: &ast.ListData{Typ: defNode.Parent.ListData.Typ}}
  210. list.SetIALAttr("id", newID)
  211. list.SetIALAttr("updated", newID[:14])
  212. list.AppendChild(defNode)
  213. refPivot.InsertAfter(list)
  214. }
  215. } else {
  216. if ast.NodeListItem == refNode.Type {
  217. newID := ast.NewNodeID()
  218. list := &ast.Node{ID: newID, Type: ast.NodeList, ListData: &ast.ListData{Typ: refNode.Parent.ListData.Typ}}
  219. list.SetIALAttr("id", newID)
  220. list.SetIALAttr("updated", newID[:14])
  221. list.AppendChild(refNode)
  222. defNode.InsertAfter(list)
  223. newID = ast.NewNodeID()
  224. li := &ast.Node{ID: newID, Type: ast.NodeListItem, ListData: &ast.ListData{Typ: refNode.Parent.ListData.Typ}}
  225. li.SetIALAttr("id", newID)
  226. li.SetIALAttr("updated", newID[:14])
  227. li.AppendChild(defNode)
  228. for i := len(defNodeChildren) - 1; -1 < i; i-- {
  229. defNode.InsertAfter(defNodeChildren[i])
  230. }
  231. refPivot.InsertAfter(li)
  232. } else {
  233. defNode.InsertAfter(refNode)
  234. refPivot.InsertAfter(defNode)
  235. for i := len(defNodeChildren) - 1; -1 < i; i-- {
  236. defNode.InsertAfter(defNodeChildren[i])
  237. }
  238. }
  239. }
  240. refPivot.Unlink()
  241. if err = indexWriteJSONQueue(refTree); nil != err {
  242. return
  243. }
  244. if !sameTree {
  245. if err = indexWriteJSONQueue(defTree); nil != err {
  246. return
  247. }
  248. }
  249. WaitForWritingFiles()
  250. util.ReloadUI()
  251. return
  252. }
  253. func GetHeadingDeleteTransaction(id string) (transaction *Transaction, err error) {
  254. tree, err := loadTreeByBlockID(id)
  255. if nil != err {
  256. return
  257. }
  258. node := treenode.GetNodeInTree(tree, id)
  259. if nil == node {
  260. err = errors.New(fmt.Sprintf(Conf.Language(15), id))
  261. return
  262. }
  263. if ast.NodeHeading != node.Type {
  264. return
  265. }
  266. var nodes []*ast.Node
  267. nodes = append(nodes, node)
  268. nodes = append(nodes, treenode.HeadingChildren(node)...)
  269. transaction = &Transaction{}
  270. luteEngine := util.NewLute()
  271. for _, n := range nodes {
  272. op := &Operation{}
  273. op.ID = n.ID
  274. op.Action = "delete"
  275. transaction.DoOperations = append(transaction.DoOperations, op)
  276. op = &Operation{}
  277. op.ID = n.ID
  278. if nil != n.Parent {
  279. op.ParentID = n.Parent.ID
  280. }
  281. if nil != n.Previous {
  282. op.PreviousID = n.Previous.ID
  283. }
  284. op.Action = "insert"
  285. op.Data = luteEngine.RenderNodeBlockDOM(n)
  286. transaction.UndoOperations = append(transaction.UndoOperations, op)
  287. }
  288. return
  289. }
  290. func GetHeadingChildrenIDs(id string) (ret []string) {
  291. tree, err := loadTreeByBlockID(id)
  292. if nil != err {
  293. return
  294. }
  295. heading := treenode.GetNodeInTree(tree, id)
  296. if nil == heading || ast.NodeHeading != heading.Type {
  297. return
  298. }
  299. children := treenode.HeadingChildren(heading)
  300. nodes := append([]*ast.Node{}, children...)
  301. for _, n := range nodes {
  302. ret = append(ret, n.ID)
  303. }
  304. return
  305. }
  306. func GetHeadingChildrenDOM(id string) (ret string) {
  307. tree, err := loadTreeByBlockID(id)
  308. if nil != err {
  309. return
  310. }
  311. heading := treenode.GetNodeInTree(tree, id)
  312. if nil == heading || ast.NodeHeading != heading.Type {
  313. return
  314. }
  315. nodes := append([]*ast.Node{}, heading)
  316. children := treenode.HeadingChildren(heading)
  317. nodes = append(nodes, children...)
  318. luteEngine := util.NewLute()
  319. ret = renderBlockDOMByNodes(nodes, luteEngine)
  320. return
  321. }
  322. func GetHeadingLevelTransaction(id string, level int) (transaction *Transaction, err error) {
  323. tree, err := loadTreeByBlockID(id)
  324. if nil != err {
  325. return
  326. }
  327. node := treenode.GetNodeInTree(tree, id)
  328. if nil == node {
  329. err = errors.New(fmt.Sprintf(Conf.Language(15), id))
  330. return
  331. }
  332. if ast.NodeHeading != node.Type {
  333. return
  334. }
  335. hLevel := node.HeadingLevel
  336. if hLevel == level {
  337. return
  338. }
  339. diff := level - hLevel
  340. var children, childrenHeadings []*ast.Node
  341. children = append(children, node)
  342. children = append(children, treenode.HeadingChildren(node)...)
  343. for _, c := range children {
  344. ccH := c.ChildrenByType(ast.NodeHeading)
  345. childrenHeadings = append(childrenHeadings, ccH...)
  346. }
  347. transaction = &Transaction{}
  348. luteEngine := util.NewLute()
  349. for _, c := range childrenHeadings {
  350. op := &Operation{}
  351. op.ID = c.ID
  352. op.Action = "update"
  353. op.Data = luteEngine.RenderNodeBlockDOM(c)
  354. transaction.UndoOperations = append(transaction.UndoOperations, op)
  355. c.HeadingLevel += diff
  356. if 6 < c.HeadingLevel {
  357. c.HeadingLevel = 6
  358. } else if 1 > c.HeadingLevel {
  359. c.HeadingLevel = 1
  360. }
  361. op = &Operation{}
  362. op.ID = c.ID
  363. op.Action = "update"
  364. op.Data = luteEngine.RenderNodeBlockDOM(c)
  365. transaction.DoOperations = append(transaction.DoOperations, op)
  366. }
  367. return
  368. }
  369. func GetBlockDOM(id string) (ret string) {
  370. if "" == id {
  371. return
  372. }
  373. tree, err := loadTreeByBlockID(id)
  374. if nil != err {
  375. return
  376. }
  377. node := treenode.GetNodeInTree(tree, id)
  378. luteEngine := NewLute()
  379. ret = luteEngine.RenderNodeBlockDOM(node)
  380. return
  381. }
  382. func GetBlockKramdown(id string) (ret string) {
  383. if "" == id {
  384. return
  385. }
  386. tree, err := loadTreeByBlockID(id)
  387. if nil != err {
  388. return
  389. }
  390. addBlockIALNodes(tree, false)
  391. node := treenode.GetNodeInTree(tree, id)
  392. root := &ast.Node{Type: ast.NodeDocument}
  393. root.AppendChild(node.Next) // IAL
  394. root.PrependChild(node)
  395. luteEngine := NewLute()
  396. ret = treenode.ExportNodeStdMd(root, luteEngine)
  397. return
  398. }
  399. type ChildBlock struct {
  400. ID string `json:"id"`
  401. Type string `json:"type"`
  402. SubType string `json:"subType,omitempty"`
  403. }
  404. func GetChildBlocks(id string) (ret []*ChildBlock) {
  405. ret = []*ChildBlock{}
  406. if "" == id {
  407. return
  408. }
  409. tree, err := loadTreeByBlockID(id)
  410. if nil != err {
  411. return
  412. }
  413. node := treenode.GetNodeInTree(tree, id)
  414. if nil == node {
  415. return
  416. }
  417. if ast.NodeHeading == node.Type {
  418. children := treenode.HeadingChildren(node)
  419. for _, c := range children {
  420. ret = append(ret, &ChildBlock{
  421. ID: c.ID,
  422. Type: treenode.TypeAbbr(c.Type.String()),
  423. SubType: treenode.SubTypeAbbr(c),
  424. })
  425. }
  426. return
  427. }
  428. if !node.IsContainerBlock() {
  429. return
  430. }
  431. for c := node.FirstChild; nil != c; c = c.Next {
  432. if !c.IsBlock() {
  433. continue
  434. }
  435. ret = append(ret, &ChildBlock{
  436. ID: c.ID,
  437. Type: treenode.TypeAbbr(c.Type.String()),
  438. SubType: treenode.SubTypeAbbr(c),
  439. })
  440. }
  441. return
  442. }
  443. func GetBlock(id string, tree *parse.Tree) (ret *Block, err error) {
  444. ret, err = getBlock(id, tree)
  445. return
  446. }
  447. func getBlock(id string, tree *parse.Tree) (ret *Block, err error) {
  448. if "" == id {
  449. return
  450. }
  451. if nil == tree {
  452. tree, err = loadTreeByBlockID(id)
  453. if nil != err {
  454. time.Sleep(1 * time.Second)
  455. tree, err = loadTreeByBlockID(id)
  456. if nil != err {
  457. return
  458. }
  459. }
  460. }
  461. node := treenode.GetNodeInTree(tree, id)
  462. if nil == node {
  463. err = ErrBlockNotFound
  464. return
  465. }
  466. sqlBlock := sql.BuildBlockFromNode(node, tree)
  467. if nil == sqlBlock {
  468. return
  469. }
  470. ret = fromSQLBlock(sqlBlock, "", 0)
  471. return
  472. }
  473. func getEmbeddedBlock(embedBlockID string, trees map[string]*parse.Tree, sqlBlock *sql.Block, headingMode int, breadcrumb bool) (block *Block, blockPaths []*BlockPath) {
  474. tree, _ := trees[sqlBlock.RootID]
  475. if nil == tree {
  476. tree, _ = loadTreeByBlockID(sqlBlock.RootID)
  477. }
  478. if nil == tree {
  479. return
  480. }
  481. def := treenode.GetNodeInTree(tree, sqlBlock.ID)
  482. if nil == def {
  483. return
  484. }
  485. var unlinks, nodes []*ast.Node
  486. ast.Walk(def, func(n *ast.Node, entering bool) ast.WalkStatus {
  487. if !entering {
  488. return ast.WalkContinue
  489. }
  490. if ast.NodeHeading == n.Type {
  491. if "1" == n.IALAttr("fold") {
  492. children := treenode.HeadingChildren(n)
  493. for _, c := range children {
  494. unlinks = append(unlinks, c)
  495. }
  496. }
  497. }
  498. return ast.WalkContinue
  499. })
  500. for _, n := range unlinks {
  501. n.Unlink()
  502. }
  503. nodes = append(nodes, def)
  504. if 0 == headingMode && ast.NodeHeading == def.Type && "1" != def.IALAttr("fold") {
  505. children := treenode.HeadingChildren(def)
  506. for _, c := range children {
  507. if "1" == c.IALAttr("heading-fold") {
  508. // 嵌入块包含折叠标题时不应该显示其下方块 https://github.com/siyuan-note/siyuan/issues/4765
  509. continue
  510. }
  511. nodes = append(nodes, c)
  512. }
  513. }
  514. b := treenode.GetBlockTree(def.ID)
  515. if nil == b {
  516. return
  517. }
  518. // 嵌入块查询结果中显示块引用计数 https://github.com/siyuan-note/siyuan/issues/7191
  519. var defIDs []string
  520. for _, n := range nodes {
  521. defIDs = append(defIDs, n.ID)
  522. }
  523. refCount := sql.QueryRefCount(defIDs)
  524. for _, n := range nodes {
  525. if cnt := refCount[n.ID]; 0 < cnt {
  526. n.SetIALAttr("refcount", strconv.Itoa(cnt))
  527. }
  528. }
  529. luteEngine := NewLute()
  530. luteEngine.RenderOptions.ProtyleContenteditable = false // 不可编辑
  531. dom := renderBlockDOMByNodes(nodes, luteEngine)
  532. content := renderBlockContentByNodes(nodes)
  533. block = &Block{Box: def.Box, Path: def.Path, HPath: b.HPath, ID: def.ID, Type: def.Type.String(), Content: dom, Markdown: content /* 这里使用 Markdown 字段来临时存储 content */}
  534. if breadcrumb {
  535. blockPaths = buildBlockBreadcrumb(def, nil)
  536. }
  537. if 1 > len(blockPaths) {
  538. blockPaths = []*BlockPath{}
  539. }
  540. return
  541. }