A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 138 lines 4.1 kB view raw
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}