2023-06-24 20:39:55 +08:00
|
|
|
|
// SiYuan - Refactor your thinking
|
2022-05-26 15:18:53 +08:00
|
|
|
|
// Copyright (c) 2020-present, b3log.org
|
|
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
package model
|
|
|
|
|
|
|
|
|
|
import (
|
2024-10-02 22:42:59 +08:00
|
|
|
|
"bytes"
|
2022-09-27 21:52:30 +08:00
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2023-11-24 12:44:51 +08:00
|
|
|
|
"strings"
|
2023-01-25 22:58:25 +08:00
|
|
|
|
"time"
|
2022-09-27 21:52:30 +08:00
|
|
|
|
|
2025-03-16 20:04:08 +08:00
|
|
|
|
"github.com/88250/gulu"
|
2022-05-26 15:18:53 +08:00
|
|
|
|
"github.com/88250/lute/ast"
|
2022-10-02 20:40:26 +08:00
|
|
|
|
"github.com/88250/lute/parse"
|
2024-12-27 10:53:37 +08:00
|
|
|
|
"github.com/88250/lute/render"
|
2024-09-11 11:44:06 +08:00
|
|
|
|
"github.com/open-spaced-repetition/go-fsrs/v3"
|
2024-05-09 23:20:28 +08:00
|
|
|
|
"github.com/siyuan-note/siyuan/kernel/filesys"
|
2022-05-26 15:18:53 +08:00
|
|
|
|
"github.com/siyuan-note/siyuan/kernel/sql"
|
|
|
|
|
"github.com/siyuan-note/siyuan/kernel/treenode"
|
2022-10-02 20:40:26 +08:00
|
|
|
|
"github.com/siyuan-note/siyuan/kernel/util"
|
2022-05-26 15:18:53 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Block 描述了内容块。
|
|
|
|
|
type Block struct {
|
2022-08-28 10:43:56 +08:00
|
|
|
|
Box string `json:"box"`
|
|
|
|
|
Path string `json:"path"`
|
|
|
|
|
HPath string `json:"hPath"`
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
RootID string `json:"rootID"`
|
|
|
|
|
ParentID string `json:"parentID"`
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
Alias string `json:"alias"`
|
|
|
|
|
Memo string `json:"memo"`
|
|
|
|
|
Tag string `json:"tag"`
|
|
|
|
|
Content string `json:"content"`
|
|
|
|
|
FContent string `json:"fcontent"`
|
|
|
|
|
Markdown string `json:"markdown"`
|
|
|
|
|
Folded bool `json:"folded"`
|
|
|
|
|
Type string `json:"type"`
|
|
|
|
|
SubType string `json:"subType"`
|
|
|
|
|
RefText string `json:"refText"`
|
|
|
|
|
Defs []*Block `json:"-"` // 当前块引用了这些块,避免序列化 JSON 时产生循环引用
|
|
|
|
|
Refs []*Block `json:"refs"` // 当前块被这些块引用
|
|
|
|
|
DefID string `json:"defID"`
|
|
|
|
|
DefPath string `json:"defPath"`
|
|
|
|
|
IAL map[string]string `json:"ial"`
|
|
|
|
|
Children []*Block `json:"children"`
|
|
|
|
|
Depth int `json:"depth"`
|
|
|
|
|
Count int `json:"count"`
|
2022-11-30 12:11:49 +08:00
|
|
|
|
Sort int `json:"sort"`
|
2022-12-02 23:38:57 +08:00
|
|
|
|
Created string `json:"created"`
|
|
|
|
|
Updated string `json:"updated"`
|
2023-02-24 23:24:40 +08:00
|
|
|
|
|
2023-11-10 20:21:56 +08:00
|
|
|
|
RiffCardID string `json:"riffCardID"`
|
|
|
|
|
RiffCard *RiffCard `json:"riffCard"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type RiffCard struct {
|
2024-03-07 20:36:47 +08:00
|
|
|
|
Due time.Time `json:"due"`
|
|
|
|
|
Reps uint64 `json:"reps"`
|
|
|
|
|
Lapses uint64 `json:"lapses"`
|
|
|
|
|
State fsrs.State `json:"state"`
|
|
|
|
|
LastReview time.Time `json:"lastReview"`
|
2023-11-10 20:21:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-26 15:18:53 +08:00
|
|
|
|
func (block *Block) IsContainerBlock() bool {
|
|
|
|
|
switch block.Type {
|
|
|
|
|
case "NodeDocument", "NodeBlockquote", "NodeList", "NodeListItem", "NodeSuperBlock":
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-04 23:19:36 +08:00
|
|
|
|
func (block *Block) IsDoc() bool {
|
|
|
|
|
return "NodeDocument" == block.Type
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-26 15:18:53 +08:00
|
|
|
|
type Path struct {
|
2022-10-02 11:17:12 +08:00
|
|
|
|
ID string `json:"id"` // 块 ID
|
|
|
|
|
Box string `json:"box"` // 块 Box
|
|
|
|
|
Name string `json:"name"` // 当前路径
|
2022-10-05 21:22:48 +08:00
|
|
|
|
HPath string `json:"hPath"` // 人类可读路径
|
2022-10-02 11:17:12 +08:00
|
|
|
|
Type string `json:"type"` // "path"
|
|
|
|
|
NodeType string `json:"nodeType"` // 节点类型
|
|
|
|
|
SubType string `json:"subType"` // 节点子类型
|
|
|
|
|
Blocks []*Block `json:"blocks,omitempty"` // 子块节点
|
|
|
|
|
Children []*Path `json:"children,omitempty"` // 子路径节点
|
|
|
|
|
Depth int `json:"depth"` // 层级深度
|
|
|
|
|
Count int `json:"count"` // 子块计数
|
2022-10-05 10:29:00 +08:00
|
|
|
|
|
|
|
|
|
Updated string `json:"updated"` // 更新时间
|
|
|
|
|
Created string `json:"created"` // 创建时间
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-03-16 20:04:08 +08:00
|
|
|
|
func CheckBlockRef(ids []string) bool {
|
|
|
|
|
bts := treenode.GetBlockTrees(ids)
|
|
|
|
|
|
|
|
|
|
var rootIDs, blockIDs []string
|
|
|
|
|
for _, bt := range bts {
|
|
|
|
|
if "d" == bt.Type {
|
|
|
|
|
rootIDs = append(rootIDs, bt.ID)
|
|
|
|
|
} else {
|
|
|
|
|
blockIDs = append(blockIDs, bt.ID)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
rootIDs = gulu.Str.RemoveDuplicatedElem(rootIDs)
|
|
|
|
|
blockIDs = gulu.Str.RemoveDuplicatedElem(blockIDs)
|
|
|
|
|
|
|
|
|
|
existRef := func(refCounts map[string]int) bool {
|
|
|
|
|
for _, refCount := range refCounts {
|
|
|
|
|
if 0 < refCount {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, rootID := range rootIDs {
|
|
|
|
|
refCounts := sql.QueryRootChildrenRefCount(rootID)
|
|
|
|
|
if existRef(refCounts) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
refCounts := sql.QueryRefCount(blockIDs)
|
|
|
|
|
if existRef(refCounts) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO 还需要考虑容器块的子块引用计数 https://github.com/siyuan-note/siyuan/issues/13396
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-09 23:20:28 +08:00
|
|
|
|
type BlockTreeInfo struct {
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
Type string `json:"type"`
|
|
|
|
|
ParentID string `json:"parentID"`
|
|
|
|
|
ParentType string `json:"parentType"`
|
|
|
|
|
PreviousID string `json:"previousID"`
|
|
|
|
|
PreviousType string `json:"previousType"`
|
|
|
|
|
NextID string `json:"nextID"`
|
|
|
|
|
NextType string `json:"nextType"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetBlockTreeInfos(ids []string) (ret map[string]*BlockTreeInfo) {
|
|
|
|
|
ret = map[string]*BlockTreeInfo{}
|
2024-10-19 16:45:37 +08:00
|
|
|
|
trees := filesys.LoadTrees(ids)
|
|
|
|
|
for id, tree := range trees {
|
2024-05-09 23:20:28 +08:00
|
|
|
|
node := treenode.GetNodeInTree(tree, id)
|
|
|
|
|
if nil == node {
|
|
|
|
|
ret[id] = &BlockTreeInfo{ID: id}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bti := &BlockTreeInfo{ID: id, Type: node.Type.String()}
|
|
|
|
|
ret[id] = bti
|
|
|
|
|
parent := treenode.ParentBlock(node)
|
|
|
|
|
if nil != parent {
|
|
|
|
|
bti.ParentID = parent.ID
|
|
|
|
|
bti.ParentType = parent.Type.String()
|
|
|
|
|
}
|
|
|
|
|
previous := treenode.PreviousBlock(node)
|
|
|
|
|
if nil != previous {
|
|
|
|
|
bti.PreviousID = previous.ID
|
|
|
|
|
bti.PreviousType = previous.Type.String()
|
|
|
|
|
}
|
|
|
|
|
next := treenode.NextBlock(node)
|
|
|
|
|
if nil != next {
|
|
|
|
|
bti.NextID = next.ID
|
|
|
|
|
bti.NextType = next.Type.String()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-30 21:33:50 +08:00
|
|
|
|
func GetBlockSiblingID(id string) (parent, previous, next string) {
|
2024-03-10 23:27:13 +08:00
|
|
|
|
tree, err := LoadTreeByBlockID(id)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2024-04-30 21:33:50 +08:00
|
|
|
|
return
|
2023-10-28 11:25:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node := treenode.GetNodeInTree(tree, id)
|
|
|
|
|
if nil == node {
|
2024-04-30 21:33:50 +08:00
|
|
|
|
return
|
2023-10-28 11:25:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-30 21:33:50 +08:00
|
|
|
|
if !node.IsBlock() {
|
|
|
|
|
return
|
|
|
|
|
}
|
2023-10-28 16:35:18 +08:00
|
|
|
|
|
2024-04-30 23:25:33 +08:00
|
|
|
|
parentListCount := 0
|
2024-05-08 21:39:29 +08:00
|
|
|
|
var parentListItem, current *ast.Node
|
2024-04-30 22:34:04 +08:00
|
|
|
|
for p := node.Parent; nil != p; p = p.Parent {
|
2024-04-30 23:39:33 +08:00
|
|
|
|
if ast.NodeListItem == p.Type {
|
2024-04-30 23:25:33 +08:00
|
|
|
|
parentListCount++
|
|
|
|
|
if 1 < parentListCount {
|
2024-04-30 23:39:33 +08:00
|
|
|
|
parentListItem = p
|
2024-04-30 23:25:33 +08:00
|
|
|
|
break
|
|
|
|
|
}
|
2024-04-30 23:39:33 +08:00
|
|
|
|
current = p.Parent
|
2024-04-30 22:34:04 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-08 21:39:29 +08:00
|
|
|
|
if nil != parentListItem {
|
|
|
|
|
parent = parentListItem.ID
|
2024-04-30 23:25:33 +08:00
|
|
|
|
|
2024-04-30 23:39:33 +08:00
|
|
|
|
if parentListItem.Previous != nil {
|
|
|
|
|
previous = parentListItem.Previous.ID
|
2024-05-09 23:20:28 +08:00
|
|
|
|
if flb := treenode.FirstChildBlock(parentListItem.Previous); nil != flb {
|
2024-04-30 23:25:33 +08:00
|
|
|
|
previous = flb.ID
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-30 23:39:33 +08:00
|
|
|
|
|
|
|
|
|
if parentListItem.Next != nil {
|
|
|
|
|
next = parentListItem.Next.ID
|
2024-05-09 23:20:28 +08:00
|
|
|
|
if flb := treenode.FirstChildBlock(parentListItem.Next); nil != flb {
|
2024-04-30 23:25:33 +08:00
|
|
|
|
next = flb.ID
|
|
|
|
|
}
|
2024-04-30 22:34:04 +08:00
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-30 23:39:33 +08:00
|
|
|
|
if nil == current {
|
|
|
|
|
current = node
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if nil != current.Parent && current.Parent.IsBlock() {
|
|
|
|
|
parent = current.Parent.ID
|
2024-05-09 23:20:28 +08:00
|
|
|
|
if flb := treenode.FirstChildBlock(current.Parent); nil != flb {
|
2024-04-30 23:25:33 +08:00
|
|
|
|
parent = flb.ID
|
|
|
|
|
}
|
2024-04-30 21:33:50 +08:00
|
|
|
|
|
2024-04-30 23:39:33 +08:00
|
|
|
|
if ast.NodeDocument == current.Parent.Type {
|
2024-05-01 10:18:10 +08:00
|
|
|
|
parent = current.Parent.ID
|
|
|
|
|
|
2024-04-30 23:39:33 +08:00
|
|
|
|
if nil != current.Previous && current.Previous.IsBlock() {
|
|
|
|
|
previous = current.Previous.ID
|
2024-05-09 23:20:28 +08:00
|
|
|
|
if flb := treenode.FirstChildBlock(current.Previous); nil != flb {
|
2024-04-30 23:25:33 +08:00
|
|
|
|
previous = flb.ID
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-30 23:39:33 +08:00
|
|
|
|
if nil != current.Next && current.Next.IsBlock() {
|
|
|
|
|
next = current.Next.ID
|
2024-05-09 23:20:28 +08:00
|
|
|
|
if flb := treenode.FirstChildBlock(current.Next); nil != flb {
|
2024-04-30 23:25:33 +08:00
|
|
|
|
next = flb.ID
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2024-04-30 23:39:33 +08:00
|
|
|
|
if nil != current.Parent.Previous && current.Parent.Previous.IsBlock() {
|
|
|
|
|
previous = current.Parent.Previous.ID
|
2024-05-09 23:20:28 +08:00
|
|
|
|
if flb := treenode.FirstChildBlock(current.Parent.Previous); nil != flb {
|
2024-04-30 23:25:33 +08:00
|
|
|
|
previous = flb.ID
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-30 23:39:33 +08:00
|
|
|
|
if nil != current.Parent.Next && current.Parent.Next.IsBlock() {
|
|
|
|
|
next = current.Parent.Next.ID
|
2024-05-09 23:20:28 +08:00
|
|
|
|
if flb := treenode.FirstChildBlock(current.Parent.Next); nil != flb {
|
2024-04-30 23:25:33 +08:00
|
|
|
|
next = flb.ID
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-30 21:33:50 +08:00
|
|
|
|
}
|
|
|
|
|
return
|
2023-10-28 11:25:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-25 20:19:44 +08:00
|
|
|
|
func GetUnfoldedParentID(id string) (parentID string) {
|
|
|
|
|
tree, err := LoadTreeByBlockID(id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node := treenode.GetNodeInTree(tree, id)
|
|
|
|
|
if nil == node {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !node.IsBlock() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-26 09:49:50 +08:00
|
|
|
|
var firstFoldedParent *ast.Node
|
2024-12-25 20:19:44 +08:00
|
|
|
|
for parent := treenode.HeadingParent(node); nil != parent && ast.NodeDocument != parent.Type; parent = treenode.HeadingParent(parent) {
|
2024-12-26 09:49:50 +08:00
|
|
|
|
if "1" == parent.IALAttr("fold") {
|
|
|
|
|
firstFoldedParent = parent
|
2024-12-26 10:34:50 +08:00
|
|
|
|
parentID = firstFoldedParent.ID
|
2025-02-13 11:39:31 +08:00
|
|
|
|
} else {
|
2024-12-26 09:49:50 +08:00
|
|
|
|
if nil != firstFoldedParent {
|
|
|
|
|
parentID = firstFoldedParent.ID
|
|
|
|
|
} else {
|
2025-02-24 22:01:58 +08:00
|
|
|
|
parentID = id
|
2024-12-26 09:49:50 +08:00
|
|
|
|
}
|
|
|
|
|
return
|
2024-12-25 20:19:44 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-13 11:39:31 +08:00
|
|
|
|
if "" == parentID {
|
|
|
|
|
parentID = id
|
|
|
|
|
}
|
2024-12-25 20:19:44 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-29 10:20:37 +08:00
|
|
|
|
func IsBlockFolded(id string) (isFolded, isRoot bool) {
|
|
|
|
|
tree, _ := LoadTreeByBlockID(id)
|
|
|
|
|
if nil == tree {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if tree.Root.ID == id {
|
|
|
|
|
isRoot = true
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-03 23:37:46 +08:00
|
|
|
|
for i := 0; i < 32; i++ {
|
|
|
|
|
b, _ := getBlock(id, nil)
|
|
|
|
|
if nil == b {
|
2024-05-29 10:20:37 +08:00
|
|
|
|
return
|
2023-02-03 23:37:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if "1" == b.IAL["fold"] {
|
2024-05-29 10:20:37 +08:00
|
|
|
|
isFolded = true
|
|
|
|
|
return
|
2023-02-03 23:37:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
id = b.ParentID
|
2024-05-29 10:20:37 +08:00
|
|
|
|
|
2023-02-03 23:37:46 +08:00
|
|
|
|
}
|
2024-05-29 10:20:37 +08:00
|
|
|
|
return
|
2023-02-03 23:37:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-26 15:18:53 +08:00
|
|
|
|
func RecentUpdatedBlocks() (ret []*Block) {
|
|
|
|
|
ret = []*Block{}
|
|
|
|
|
|
2024-10-02 22:42:59 +08:00
|
|
|
|
sqlStmt := "SELECT * FROM blocks WHERE type = 'p' AND length > 1"
|
2024-11-22 20:15:47 +08:00
|
|
|
|
if util.ContainerIOS == util.Container || util.ContainerAndroid == util.Container || util.ContainerHarmony == util.Container {
|
2024-10-02 22:42:59 +08:00
|
|
|
|
sqlStmt = "SELECT * FROM blocks WHERE type = 'd'"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ignoreLines := getSearchIgnoreLines(); 0 < len(ignoreLines) {
|
|
|
|
|
// Support ignore search results https://github.com/siyuan-note/siyuan/issues/10089
|
|
|
|
|
buf := bytes.Buffer{}
|
|
|
|
|
for _, line := range ignoreLines {
|
|
|
|
|
buf.WriteString(" AND ")
|
|
|
|
|
buf.WriteString(line)
|
|
|
|
|
}
|
|
|
|
|
sqlStmt += buf.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sqlStmt += " ORDER BY updated DESC"
|
|
|
|
|
sqlBlocks := sql.SelectBlocksRawStmt(sqlStmt, 1, 16)
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if 1 > len(sqlBlocks) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = fromSQLBlocks(&sqlBlocks, "", 0)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-13 09:52:41 +08:00
|
|
|
|
func TransferBlockRef(fromID, toID string, refIDs []string) (err error) {
|
2024-03-10 23:27:13 +08:00
|
|
|
|
toTree, _ := LoadTreeByBlockID(toID)
|
2023-02-06 18:03:29 +08:00
|
|
|
|
if nil == toTree {
|
|
|
|
|
err = ErrBlockNotFound
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
toNode := treenode.GetNodeInTree(toTree, toID)
|
|
|
|
|
if nil == toNode {
|
|
|
|
|
err = ErrBlockNotFound
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
toRefText := getNodeRefText(toNode)
|
|
|
|
|
|
2023-02-06 19:07:29 +08:00
|
|
|
|
util.PushMsg(Conf.Language(116), 7000)
|
|
|
|
|
|
2023-06-13 09:52:41 +08:00
|
|
|
|
if 1 > len(refIDs) { // 如果不指定 refIDs,则转移所有引用了 fromID 的块
|
2025-01-12 18:46:35 +08:00
|
|
|
|
refIDs = sql.QueryRefIDsByDefID(fromID, false)
|
2023-06-13 09:52:41 +08:00
|
|
|
|
}
|
2024-10-19 17:33:04 +08:00
|
|
|
|
|
|
|
|
|
trees := filesys.LoadTrees(refIDs)
|
|
|
|
|
for refID, tree := range trees {
|
2023-02-06 18:03:29 +08:00
|
|
|
|
if nil == tree {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2024-10-19 17:33:04 +08:00
|
|
|
|
|
2023-02-06 18:03:29 +08:00
|
|
|
|
node := treenode.GetNodeInTree(tree, refID)
|
|
|
|
|
textMarks := node.ChildrenByType(ast.NodeTextMark)
|
|
|
|
|
for _, textMark := range textMarks {
|
|
|
|
|
if textMark.IsTextMarkType("block-ref") && textMark.TextMarkBlockRefID == fromID {
|
|
|
|
|
textMark.TextMarkBlockRefID = toID
|
2023-02-06 18:08:41 +08:00
|
|
|
|
if "d" == textMark.TextMarkBlockRefSubtype {
|
2023-02-06 18:03:29 +08:00
|
|
|
|
textMark.TextMarkTextContent = toRefText
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err = indexWriteTreeUpsertQueue(tree); err != nil {
|
2023-02-06 18:03:29 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-06 18:12:12 +08:00
|
|
|
|
|
2024-10-14 20:39:16 +08:00
|
|
|
|
sql.FlushQueue()
|
2023-02-06 18:03:29 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-02 22:44:20 +08:00
|
|
|
|
func SwapBlockRef(refID, defID string, includeChildren bool) (err error) {
|
2024-03-10 23:27:13 +08:00
|
|
|
|
refTree, err := LoadTreeByBlockID(refID)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2022-10-02 20:40:26 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
refNode := treenode.GetNodeInTree(refTree, refID)
|
2022-10-02 20:42:15 +08:00
|
|
|
|
if nil == refNode {
|
|
|
|
|
return
|
|
|
|
|
}
|
2022-10-02 22:09:54 +08:00
|
|
|
|
if ast.NodeListItem == refNode.Parent.Type {
|
|
|
|
|
refNode = refNode.Parent
|
|
|
|
|
}
|
2024-03-10 23:27:13 +08:00
|
|
|
|
defTree, err := LoadTreeByBlockID(defID)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2022-10-02 20:40:26 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
2022-10-04 12:26:44 +08:00
|
|
|
|
sameTree := defTree.ID == refTree.ID
|
|
|
|
|
var defNode *ast.Node
|
|
|
|
|
if !sameTree {
|
|
|
|
|
defNode = treenode.GetNodeInTree(defTree, defID)
|
|
|
|
|
} else {
|
|
|
|
|
defNode = treenode.GetNodeInTree(refTree, defID)
|
|
|
|
|
}
|
2022-10-02 20:42:15 +08:00
|
|
|
|
if nil == defNode {
|
|
|
|
|
return
|
|
|
|
|
}
|
2022-10-02 22:48:36 +08:00
|
|
|
|
var defNodeChildren []*ast.Node
|
2022-10-02 22:09:54 +08:00
|
|
|
|
if ast.NodeListItem == defNode.Parent.Type {
|
|
|
|
|
defNode = defNode.Parent
|
2022-10-02 23:27:00 +08:00
|
|
|
|
} else if ast.NodeHeading == defNode.Type && includeChildren {
|
|
|
|
|
defNodeChildren = treenode.HeadingChildren(defNode)
|
|
|
|
|
}
|
|
|
|
|
if ast.NodeListItem == defNode.Type {
|
2022-10-02 23:09:11 +08:00
|
|
|
|
for c := defNode.FirstChild; nil != c; c = c.Next {
|
|
|
|
|
if ast.NodeList == c.Type {
|
|
|
|
|
defNodeChildren = append(defNodeChildren, c)
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-02 22:09:54 +08:00
|
|
|
|
}
|
2022-10-02 20:40:26 +08:00
|
|
|
|
|
2023-02-04 10:30:55 +08:00
|
|
|
|
refreshUpdated(defNode)
|
|
|
|
|
refreshUpdated(refNode)
|
|
|
|
|
|
2024-11-10 12:04:56 +08:00
|
|
|
|
refPivot := treenode.NewParagraph("")
|
2022-10-02 20:40:26 +08:00
|
|
|
|
refNode.InsertBefore(refPivot)
|
|
|
|
|
|
|
|
|
|
if ast.NodeListItem == defNode.Type {
|
2022-10-02 22:09:54 +08:00
|
|
|
|
if ast.NodeListItem == refNode.Type {
|
2022-10-02 23:27:00 +08:00
|
|
|
|
if !includeChildren {
|
|
|
|
|
for _, c := range defNodeChildren {
|
|
|
|
|
refNode.AppendChild(c)
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-03 16:44:11 +08:00
|
|
|
|
defNode.InsertAfter(refNode)
|
2022-10-02 22:09:54 +08:00
|
|
|
|
refPivot.InsertAfter(defNode)
|
|
|
|
|
} else {
|
2022-10-02 20:40:26 +08:00
|
|
|
|
newID := ast.NewNodeID()
|
|
|
|
|
li := &ast.Node{ID: newID, Type: ast.NodeListItem, ListData: &ast.ListData{Typ: defNode.Parent.ListData.Typ}}
|
|
|
|
|
li.SetIALAttr("id", newID)
|
|
|
|
|
li.SetIALAttr("updated", newID[:14])
|
|
|
|
|
li.AppendChild(refNode)
|
|
|
|
|
defNode.InsertAfter(li)
|
2022-10-02 23:09:11 +08:00
|
|
|
|
if !includeChildren {
|
|
|
|
|
for _, c := range defNodeChildren {
|
|
|
|
|
li.AppendChild(c)
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-02 20:40:26 +08:00
|
|
|
|
|
|
|
|
|
newID = ast.NewNodeID()
|
|
|
|
|
list := &ast.Node{ID: newID, Type: ast.NodeList, ListData: &ast.ListData{Typ: defNode.Parent.ListData.Typ}}
|
|
|
|
|
list.SetIALAttr("id", newID)
|
|
|
|
|
list.SetIALAttr("updated", newID[:14])
|
|
|
|
|
list.AppendChild(defNode)
|
|
|
|
|
refPivot.InsertAfter(list)
|
2022-10-02 22:09:54 +08:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if ast.NodeListItem == refNode.Type {
|
|
|
|
|
newID := ast.NewNodeID()
|
|
|
|
|
list := &ast.Node{ID: newID, Type: ast.NodeList, ListData: &ast.ListData{Typ: refNode.Parent.ListData.Typ}}
|
|
|
|
|
list.SetIALAttr("id", newID)
|
|
|
|
|
list.SetIALAttr("updated", newID[:14])
|
2022-10-02 22:44:20 +08:00
|
|
|
|
list.AppendChild(refNode)
|
2022-10-02 22:09:54 +08:00
|
|
|
|
defNode.InsertAfter(list)
|
2022-10-02 22:48:36 +08:00
|
|
|
|
|
|
|
|
|
newID = ast.NewNodeID()
|
|
|
|
|
li := &ast.Node{ID: newID, Type: ast.NodeListItem, ListData: &ast.ListData{Typ: refNode.Parent.ListData.Typ}}
|
|
|
|
|
li.SetIALAttr("id", newID)
|
|
|
|
|
li.SetIALAttr("updated", newID[:14])
|
|
|
|
|
li.AppendChild(defNode)
|
2022-10-05 11:16:42 +08:00
|
|
|
|
for i := len(defNodeChildren) - 1; -1 < i; i-- {
|
|
|
|
|
defNode.InsertAfter(defNodeChildren[i])
|
|
|
|
|
}
|
2022-10-02 22:48:36 +08:00
|
|
|
|
refPivot.InsertAfter(li)
|
2022-10-02 20:40:26 +08:00
|
|
|
|
} else {
|
|
|
|
|
defNode.InsertAfter(refNode)
|
|
|
|
|
refPivot.InsertAfter(defNode)
|
2022-10-02 22:48:36 +08:00
|
|
|
|
for i := len(defNodeChildren) - 1; -1 < i; i-- {
|
2022-10-02 22:50:09 +08:00
|
|
|
|
defNode.InsertAfter(defNodeChildren[i])
|
2022-10-02 22:48:36 +08:00
|
|
|
|
}
|
2022-10-02 20:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
refPivot.Unlink()
|
|
|
|
|
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err = indexWriteTreeUpsertQueue(refTree); err != nil {
|
2022-10-02 20:40:26 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
2022-10-04 12:26:44 +08:00
|
|
|
|
if !sameTree {
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err = indexWriteTreeUpsertQueue(defTree); err != nil {
|
2022-10-04 12:26:44 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
2022-10-02 20:40:26 +08:00
|
|
|
|
}
|
2024-10-22 19:20:44 +08:00
|
|
|
|
FlushTxQueue()
|
2022-10-02 20:40:26 +08:00
|
|
|
|
util.ReloadUI()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-10 17:04:11 +08:00
|
|
|
|
func GetHeadingDeleteTransaction(id string) (transaction *Transaction, err error) {
|
2024-03-10 23:27:13 +08:00
|
|
|
|
tree, err := LoadTreeByBlockID(id)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2022-10-10 17:04:11 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node := treenode.GetNodeInTree(tree, id)
|
|
|
|
|
if nil == node {
|
|
|
|
|
err = errors.New(fmt.Sprintf(Conf.Language(15), id))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ast.NodeHeading != node.Type {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var nodes []*ast.Node
|
|
|
|
|
nodes = append(nodes, node)
|
|
|
|
|
nodes = append(nodes, treenode.HeadingChildren(node)...)
|
|
|
|
|
|
|
|
|
|
transaction = &Transaction{}
|
2023-02-10 14:28:10 +08:00
|
|
|
|
luteEngine := util.NewLute()
|
2022-10-10 17:04:11 +08:00
|
|
|
|
for _, n := range nodes {
|
|
|
|
|
op := &Operation{}
|
|
|
|
|
op.ID = n.ID
|
|
|
|
|
op.Action = "delete"
|
|
|
|
|
transaction.DoOperations = append(transaction.DoOperations, op)
|
|
|
|
|
|
|
|
|
|
op = &Operation{}
|
|
|
|
|
op.ID = n.ID
|
|
|
|
|
if nil != n.Parent {
|
|
|
|
|
op.ParentID = n.Parent.ID
|
|
|
|
|
}
|
2022-10-10 22:32:41 +08:00
|
|
|
|
if nil != n.Previous {
|
2022-10-10 17:04:11 +08:00
|
|
|
|
op.PreviousID = n.Previous.ID
|
|
|
|
|
}
|
|
|
|
|
op.Action = "insert"
|
2023-06-02 22:36:27 +08:00
|
|
|
|
op.Data = luteEngine.RenderNodeBlockDOM(n)
|
2022-10-10 17:04:11 +08:00
|
|
|
|
transaction.UndoOperations = append(transaction.UndoOperations, op)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-21 22:41:44 +08:00
|
|
|
|
func GetHeadingChildrenIDs(id string) (ret []string) {
|
2024-03-10 23:27:13 +08:00
|
|
|
|
tree, err := LoadTreeByBlockID(id)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2022-11-21 22:41:44 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
heading := treenode.GetNodeInTree(tree, id)
|
|
|
|
|
if nil == heading || ast.NodeHeading != heading.Type {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
children := treenode.HeadingChildren(heading)
|
2022-11-22 00:11:38 +08:00
|
|
|
|
nodes := append([]*ast.Node{}, children...)
|
2022-11-21 22:41:44 +08:00
|
|
|
|
for _, n := range nodes {
|
|
|
|
|
ret = append(ret, n.ID)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-27 21:52:30 +08:00
|
|
|
|
func GetHeadingChildrenDOM(id string) (ret string) {
|
2024-03-10 23:27:13 +08:00
|
|
|
|
tree, err := LoadTreeByBlockID(id)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2022-09-27 21:52:30 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
heading := treenode.GetNodeInTree(tree, id)
|
|
|
|
|
if nil == heading || ast.NodeHeading != heading.Type {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nodes := append([]*ast.Node{}, heading)
|
|
|
|
|
children := treenode.HeadingChildren(heading)
|
|
|
|
|
nodes = append(nodes, children...)
|
2024-12-11 21:53:25 +08:00
|
|
|
|
|
|
|
|
|
// 取消折叠 https://github.com/siyuan-note/siyuan/issues/13232#issuecomment-2535955152
|
|
|
|
|
for _, child := range children {
|
|
|
|
|
ast.Walk(child, func(n *ast.Node, entering bool) ast.WalkStatus {
|
|
|
|
|
if !entering {
|
|
|
|
|
return ast.WalkContinue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n.RemoveIALAttr("heading-fold")
|
|
|
|
|
n.RemoveIALAttr("fold")
|
|
|
|
|
return ast.WalkContinue
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
heading.RemoveIALAttr("fold")
|
|
|
|
|
heading.RemoveIALAttr("heading-fold")
|
|
|
|
|
|
2023-02-10 14:28:10 +08:00
|
|
|
|
luteEngine := util.NewLute()
|
2022-09-27 21:52:30 +08:00
|
|
|
|
ret = renderBlockDOMByNodes(nodes, luteEngine)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetHeadingLevelTransaction(id string, level int) (transaction *Transaction, err error) {
|
2024-03-10 23:27:13 +08:00
|
|
|
|
tree, err := LoadTreeByBlockID(id)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2022-09-27 21:52:30 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node := treenode.GetNodeInTree(tree, id)
|
|
|
|
|
if nil == node {
|
|
|
|
|
err = errors.New(fmt.Sprintf(Conf.Language(15), id))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ast.NodeHeading != node.Type {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hLevel := node.HeadingLevel
|
|
|
|
|
if hLevel == level {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
diff := level - hLevel
|
|
|
|
|
var children, childrenHeadings []*ast.Node
|
|
|
|
|
children = append(children, node)
|
|
|
|
|
children = append(children, treenode.HeadingChildren(node)...)
|
|
|
|
|
for _, c := range children {
|
2022-10-12 09:20:26 +08:00
|
|
|
|
ccH := c.ChildrenByType(ast.NodeHeading)
|
|
|
|
|
childrenHeadings = append(childrenHeadings, ccH...)
|
2022-09-27 21:52:30 +08:00
|
|
|
|
}
|
2025-07-17 17:25:47 +08:00
|
|
|
|
fillBlockRefCount(childrenHeadings)
|
2022-09-27 21:52:30 +08:00
|
|
|
|
|
|
|
|
|
transaction = &Transaction{}
|
2023-02-10 14:28:10 +08:00
|
|
|
|
luteEngine := util.NewLute()
|
2022-09-27 21:52:30 +08:00
|
|
|
|
for _, c := range childrenHeadings {
|
|
|
|
|
op := &Operation{}
|
|
|
|
|
op.ID = c.ID
|
|
|
|
|
op.Action = "update"
|
2023-06-02 22:36:27 +08:00
|
|
|
|
op.Data = luteEngine.RenderNodeBlockDOM(c)
|
2022-09-27 21:52:30 +08:00
|
|
|
|
transaction.UndoOperations = append(transaction.UndoOperations, op)
|
|
|
|
|
|
|
|
|
|
c.HeadingLevel += diff
|
|
|
|
|
if 6 < c.HeadingLevel {
|
|
|
|
|
c.HeadingLevel = 6
|
|
|
|
|
} else if 1 > c.HeadingLevel {
|
|
|
|
|
c.HeadingLevel = 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
op = &Operation{}
|
|
|
|
|
op.ID = c.ID
|
|
|
|
|
op.Action = "update"
|
2023-06-02 22:36:27 +08:00
|
|
|
|
op.Data = luteEngine.RenderNodeBlockDOM(c)
|
2022-09-27 21:52:30 +08:00
|
|
|
|
transaction.DoOperations = append(transaction.DoOperations, op)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-26 15:18:53 +08:00
|
|
|
|
func GetBlockDOM(id string) (ret string) {
|
|
|
|
|
if "" == id {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-10 23:27:13 +08:00
|
|
|
|
tree, err := LoadTreeByBlockID(id)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
node := treenode.GetNodeInTree(tree, id)
|
|
|
|
|
luteEngine := NewLute()
|
2023-06-02 22:36:27 +08:00
|
|
|
|
ret = luteEngine.RenderNodeBlockDOM(node)
|
2022-05-26 15:18:53 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-10 17:05:08 +08:00
|
|
|
|
func GetBlockDOMs(ids []string) (ret map[string]string) {
|
|
|
|
|
ret = map[string]string{}
|
|
|
|
|
if 0 == len(ids) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
luteEngine := NewLute()
|
|
|
|
|
trees := filesys.LoadTrees(ids)
|
|
|
|
|
for id, tree := range trees {
|
|
|
|
|
node := treenode.GetNodeInTree(tree, id)
|
|
|
|
|
if nil == node {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
ret[id] = luteEngine.RenderNodeBlockDOM(node)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-20 10:33:42 +08:00
|
|
|
|
func GetBlockKramdown(id, mode string) (ret string) {
|
2022-06-27 23:03:31 +08:00
|
|
|
|
if "" == id {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-10 23:27:13 +08:00
|
|
|
|
tree, err := LoadTreeByBlockID(id)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2022-06-27 23:03:31 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-28 18:56:16 +08:00
|
|
|
|
addBlockIALNodes(tree, false)
|
2022-06-27 23:03:31 +08:00
|
|
|
|
node := treenode.GetNodeInTree(tree, id)
|
2022-11-21 23:17:00 +08:00
|
|
|
|
root := &ast.Node{Type: ast.NodeDocument}
|
|
|
|
|
root.AppendChild(node.Next) // IAL
|
|
|
|
|
root.PrependChild(node)
|
2022-06-27 23:03:31 +08:00
|
|
|
|
luteEngine := NewLute()
|
2024-11-20 10:33:42 +08:00
|
|
|
|
if "md" == mode {
|
|
|
|
|
ret = treenode.ExportNodeStdMd(root, luteEngine)
|
|
|
|
|
} else {
|
|
|
|
|
tree.Root = root
|
|
|
|
|
formatRenderer := render.NewFormatRenderer(tree, luteEngine.RenderOptions)
|
|
|
|
|
ret = string(formatRenderer.Render())
|
|
|
|
|
}
|
2022-06-27 23:03:31 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-13 21:50:32 +08:00
|
|
|
|
type ChildBlock struct {
|
2024-08-16 20:49:13 +08:00
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
Type string `json:"type"`
|
|
|
|
|
SubType string `json:"subType,omitempty"`
|
|
|
|
|
Content string `json:"content,omitempty"`
|
|
|
|
|
Markdown string `json:"markdown,omitempty"`
|
2023-05-13 21:50:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetChildBlocks(id string) (ret []*ChildBlock) {
|
|
|
|
|
ret = []*ChildBlock{}
|
|
|
|
|
if "" == id {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-10 23:27:13 +08:00
|
|
|
|
tree, err := LoadTreeByBlockID(id)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2023-05-13 21:50:32 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node := treenode.GetNodeInTree(tree, id)
|
|
|
|
|
if nil == node {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ast.NodeHeading == node.Type {
|
|
|
|
|
children := treenode.HeadingChildren(node)
|
|
|
|
|
for _, c := range children {
|
2024-08-16 20:49:13 +08:00
|
|
|
|
block := sql.BuildBlockFromNode(c, tree)
|
2023-05-13 21:50:32 +08:00
|
|
|
|
ret = append(ret, &ChildBlock{
|
2024-08-16 20:49:13 +08:00
|
|
|
|
ID: c.ID,
|
|
|
|
|
Type: treenode.TypeAbbr(c.Type.String()),
|
|
|
|
|
SubType: treenode.SubTypeAbbr(c),
|
|
|
|
|
Content: block.Content,
|
|
|
|
|
Markdown: block.Markdown,
|
2023-05-13 21:50:32 +08:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !node.IsContainerBlock() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for c := node.FirstChild; nil != c; c = c.Next {
|
|
|
|
|
if !c.IsBlock() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-16 20:49:13 +08:00
|
|
|
|
block := sql.BuildBlockFromNode(c, tree)
|
2023-05-13 21:50:32 +08:00
|
|
|
|
ret = append(ret, &ChildBlock{
|
2024-08-16 20:49:13 +08:00
|
|
|
|
ID: c.ID,
|
|
|
|
|
Type: treenode.TypeAbbr(c.Type.String()),
|
|
|
|
|
SubType: treenode.SubTypeAbbr(c),
|
|
|
|
|
Content: block.Content,
|
|
|
|
|
Markdown: block.Markdown,
|
2023-05-13 21:50:32 +08:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-14 22:54:27 +08:00
|
|
|
|
func GetTailChildBlocks(id string, n int) (ret []*ChildBlock) {
|
|
|
|
|
ret = []*ChildBlock{}
|
|
|
|
|
if "" == id {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-10 23:27:13 +08:00
|
|
|
|
tree, err := LoadTreeByBlockID(id)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2023-12-14 22:54:27 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node := treenode.GetNodeInTree(tree, id)
|
|
|
|
|
if nil == node {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ast.NodeHeading == node.Type {
|
|
|
|
|
children := treenode.HeadingChildren(node)
|
|
|
|
|
for i := len(children) - 1; 0 <= i; i-- {
|
|
|
|
|
c := children[i]
|
2024-08-16 20:49:13 +08:00
|
|
|
|
block := sql.BuildBlockFromNode(c, tree)
|
2023-12-14 22:54:27 +08:00
|
|
|
|
ret = append(ret, &ChildBlock{
|
2024-08-16 20:49:13 +08:00
|
|
|
|
ID: c.ID,
|
|
|
|
|
Type: treenode.TypeAbbr(c.Type.String()),
|
|
|
|
|
SubType: treenode.SubTypeAbbr(c),
|
|
|
|
|
Content: block.Content,
|
|
|
|
|
Markdown: block.Markdown,
|
2023-12-14 22:54:27 +08:00
|
|
|
|
})
|
|
|
|
|
if n == len(ret) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !node.IsContainerBlock() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for c := node.LastChild; nil != c; c = c.Previous {
|
|
|
|
|
if !c.IsBlock() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-16 20:49:13 +08:00
|
|
|
|
block := sql.BuildBlockFromNode(c, tree)
|
2023-12-14 22:54:27 +08:00
|
|
|
|
ret = append(ret, &ChildBlock{
|
2024-08-16 20:49:13 +08:00
|
|
|
|
ID: c.ID,
|
|
|
|
|
Type: treenode.TypeAbbr(c.Type.String()),
|
|
|
|
|
SubType: treenode.SubTypeAbbr(c),
|
|
|
|
|
Content: block.Content,
|
|
|
|
|
Markdown: block.Markdown,
|
2023-12-14 22:54:27 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if n == len(ret) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-27 18:07:06 +08:00
|
|
|
|
func GetBlock(id string, tree *parse.Tree) (ret *Block, err error) {
|
|
|
|
|
ret, err = getBlock(id, tree)
|
2022-05-26 15:18:53 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-27 18:07:06 +08:00
|
|
|
|
func getBlock(id string, tree *parse.Tree) (ret *Block, err error) {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if "" == id {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-27 18:07:06 +08:00
|
|
|
|
if nil == tree {
|
2024-03-10 23:27:13 +08:00
|
|
|
|
tree, err = LoadTreeByBlockID(id)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2023-01-27 18:07:06 +08:00
|
|
|
|
time.Sleep(1 * time.Second)
|
2024-03-10 23:27:13 +08:00
|
|
|
|
tree, err = LoadTreeByBlockID(id)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2023-01-27 18:07:06 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
2023-01-25 20:32:25 +08:00
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node := treenode.GetNodeInTree(tree, id)
|
2023-01-27 18:07:06 +08:00
|
|
|
|
if nil == node {
|
|
|
|
|
err = ErrBlockNotFound
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-26 15:18:53 +08:00
|
|
|
|
sqlBlock := sql.BuildBlockFromNode(node, tree)
|
|
|
|
|
if nil == sqlBlock {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
ret = fromSQLBlock(sqlBlock, "", 0)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-18 12:03:22 +08:00
|
|
|
|
func getEmbeddedBlock(trees map[string]*parse.Tree, sqlBlock *sql.Block, headingMode int, breadcrumb bool) (block *Block, blockPaths []*BlockPath) {
|
2022-10-12 10:11:08 +08:00
|
|
|
|
tree, _ := trees[sqlBlock.RootID]
|
|
|
|
|
if nil == tree {
|
2024-03-10 23:27:13 +08:00
|
|
|
|
tree, _ = LoadTreeByBlockID(sqlBlock.RootID)
|
2022-10-12 10:11:08 +08:00
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if nil == tree {
|
|
|
|
|
return
|
|
|
|
|
}
|
2022-10-12 10:11:08 +08:00
|
|
|
|
def := treenode.GetNodeInTree(tree, sqlBlock.ID)
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if nil == def {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var unlinks, nodes []*ast.Node
|
|
|
|
|
ast.Walk(def, func(n *ast.Node, entering bool) ast.WalkStatus {
|
|
|
|
|
if !entering {
|
|
|
|
|
return ast.WalkContinue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ast.NodeHeading == n.Type {
|
|
|
|
|
if "1" == n.IALAttr("fold") {
|
2022-06-19 17:01:21 +08:00
|
|
|
|
children := treenode.HeadingChildren(n)
|
2022-05-26 15:18:53 +08:00
|
|
|
|
for _, c := range children {
|
|
|
|
|
unlinks = append(unlinks, c)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ast.WalkContinue
|
|
|
|
|
})
|
|
|
|
|
for _, n := range unlinks {
|
|
|
|
|
n.Unlink()
|
|
|
|
|
}
|
|
|
|
|
nodes = append(nodes, def)
|
|
|
|
|
if 0 == headingMode && ast.NodeHeading == def.Type && "1" != def.IALAttr("fold") {
|
|
|
|
|
children := treenode.HeadingChildren(def)
|
|
|
|
|
for _, c := range children {
|
|
|
|
|
if "1" == c.IALAttr("heading-fold") {
|
|
|
|
|
// 嵌入块包含折叠标题时不应该显示其下方块 https://github.com/siyuan-note/siyuan/issues/4765
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
nodes = append(nodes, c)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b := treenode.GetBlockTree(def.ID)
|
|
|
|
|
if nil == b {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-31 19:40:55 +08:00
|
|
|
|
// 嵌入块查询结果中显示块引用计数 https://github.com/siyuan-note/siyuan/issues/7191
|
2025-02-18 17:15:44 +08:00
|
|
|
|
fillBlockRefCount(nodes)
|
2023-01-31 19:40:55 +08:00
|
|
|
|
|
2022-05-26 15:18:53 +08:00
|
|
|
|
luteEngine := NewLute()
|
|
|
|
|
luteEngine.RenderOptions.ProtyleContenteditable = false // 不可编辑
|
|
|
|
|
dom := renderBlockDOMByNodes(nodes, luteEngine)
|
2023-01-19 20:51:32 +08:00
|
|
|
|
content := renderBlockContentByNodes(nodes)
|
|
|
|
|
block = &Block{Box: def.Box, Path: def.Path, HPath: b.HPath, ID: def.ID, Type: def.Type.String(), Content: dom, Markdown: content /* 这里使用 Markdown 字段来临时存储 content */}
|
2022-10-18 09:53:05 +08:00
|
|
|
|
|
2023-11-24 12:44:51 +08:00
|
|
|
|
if "" != sqlBlock.IAL {
|
|
|
|
|
block.IAL = map[string]string{}
|
|
|
|
|
ialStr := strings.TrimPrefix(sqlBlock.IAL, "{:")
|
|
|
|
|
ialStr = strings.TrimSuffix(ialStr, "}")
|
|
|
|
|
ial := parse.Tokens2IAL([]byte(ialStr))
|
|
|
|
|
for _, kv := range ial {
|
|
|
|
|
block.IAL[kv[0]] = kv[1]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-05 13:31:46 +08:00
|
|
|
|
if breadcrumb {
|
2024-11-10 11:51:59 +08:00
|
|
|
|
blockPaths = buildBlockBreadcrumb(def, nil, true)
|
2022-10-17 10:37:01 +08:00
|
|
|
|
}
|
|
|
|
|
if 1 > len(blockPaths) {
|
|
|
|
|
blockPaths = []*BlockPath{}
|
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
return
|
|
|
|
|
}
|