A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
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
17package model
18
19import (
20 "github.com/88250/lute/ast"
21 "github.com/88250/lute/parse"
22 "github.com/siyuan-note/siyuan/kernel/filesys"
23 "github.com/siyuan-note/siyuan/kernel/treenode"
24 "github.com/siyuan-note/siyuan/kernel/util"
25)
26
27func mergeSubDocs(rootTree *parse.Tree) (ret *parse.Tree, err error) {
28 ret = rootTree
29 rootBlock := &Block{Box: rootTree.Box, ID: rootTree.ID, Path: rootTree.Path, HPath: rootTree.HPath}
30 if err = buildBlockChildren(rootBlock); err != nil {
31 return
32 }
33
34 insertPoint := rootTree.Root.LastChild
35
36 // 跳过空段落插入点,向上寻找非空段落
37 for ; nil != insertPoint && ast.NodeParagraph == insertPoint.Type; insertPoint = insertPoint.Previous {
38 if nil != insertPoint.FirstChild {
39 break
40 }
41 }
42
43 // 导出空文档 Word 和 PDF 时合并子文档失败 https://github.com/siyuan-note/siyuan/issues/7429
44 if nil == insertPoint {
45 // 如果找不到非空段落,则使用第一个段落作为插入点
46 insertPoint = rootTree.Root.FirstChild
47 if nil == insertPoint {
48 // 如果文档为空,则创建一个空段落作为插入点
49 insertPoint = treenode.NewParagraph("")
50 rootTree.Root.AppendChild(insertPoint)
51 }
52 }
53
54 for {
55 i := 0
56 if err = walkBlock(insertPoint, rootBlock, i); err != nil {
57 return
58 }
59 if nil == rootBlock.Children {
60 break
61 }
62 }
63
64 if ast.NodeParagraph == insertPoint.Type && nil == insertPoint.FirstChild {
65 // 删除空段落
66 // Ignore the last empty paragraph block when exporting merged sub-documents https://github.com/siyuan-note/siyuan/issues/15028
67 insertPoint.Unlink()
68 }
69 return
70}
71
72func walkBlock(insertPoint *ast.Node, block *Block, level int) (err error) {
73 level++
74 for i := len(block.Children) - 1; i >= 0; i-- {
75 c := block.Children[i]
76 if err = walkBlock(insertPoint, c, level); err != nil {
77 return
78 }
79
80 nodes, loadErr := loadTreeNodes(c.Box, c.Path, level)
81 if nil != loadErr {
82 return
83 }
84
85 lastIndex := len(nodes) - 1
86 for j := lastIndex; -1 < j; j-- {
87 node := nodes[j]
88 if j == lastIndex && ast.NodeParagraph == node.Type && nil == node.FirstChild {
89 // 跳过最后一个空段落块
90 // Ignore the last empty paragraph block when exporting merged sub-documents https://github.com/siyuan-note/siyuan/issues/15028
91 continue
92 }
93 insertPoint.InsertAfter(node)
94 }
95 }
96 block.Children = nil
97 return
98}
99
100func loadTreeNodes(box string, p string, level int) (ret []*ast.Node, err error) {
101 luteEngine := NewLute()
102 tree, err := filesys.LoadTree(box, p, luteEngine)
103 if err != nil {
104 return
105 }
106
107 hLevel := level
108 if 6 < level {
109 hLevel = 6
110 }
111
112 heading := &ast.Node{ID: tree.Root.ID, Type: ast.NodeHeading, HeadingLevel: hLevel}
113 heading.AppendChild(&ast.Node{Type: ast.NodeText, Tokens: []byte(tree.Root.IALAttr("title"))})
114 tree.Root.PrependChild(heading)
115 for c := tree.Root.FirstChild; nil != c; c = c.Next {
116 ret = append(ret, c)
117 }
118 return
119}
120
121func buildBlockChildren(block *Block) (err error) {
122 files, _, err := ListDocTree(block.Box, block.Path, util.SortModeUnassigned, false, false, Conf.FileTree.MaxListCount)
123 if err != nil {
124 return
125 }
126
127 for _, f := range files {
128 childBlock := &Block{Box: block.Box, ID: f.ID, Path: f.Path}
129 block.Children = append(block.Children, childBlock)
130 }
131
132 for _, c := range block.Children {
133 if err = buildBlockChildren(c); err != nil {
134 return
135 }
136 }
137 return
138}