A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 192 lines 4.5 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 treenode 18 19import ( 20 "github.com/88250/lute/ast" 21 "github.com/88250/lute/parse" 22) 23 24func MoveFoldHeading(updateNode, oldNode *ast.Node) { 25 foldHeadings := map[string][]*ast.Node{} 26 // 找到原有节点中所有折叠标题节点的下方节点 27 ast.Walk(oldNode, func(n *ast.Node, entering bool) ast.WalkStatus { 28 if !entering { 29 return ast.WalkContinue 30 } 31 32 if ast.NodeHeading == n.Type && "1" == n.IALAttr("fold") { 33 children := HeadingChildren(n) 34 foldHeadings[n.ID] = children 35 } 36 return ast.WalkContinue 37 }) 38 39 // 将原来所有折叠标题对应的下方节点移动到新节点下 40 var updateFoldHeadings []*ast.Node 41 ast.Walk(updateNode, func(n *ast.Node, entering bool) ast.WalkStatus { 42 if !entering { 43 return ast.WalkContinue 44 } 45 46 if ast.NodeHeading == n.Type && "1" == n.IALAttr("fold") { 47 updateFoldHeadings = append(updateFoldHeadings, n) 48 } 49 return ast.WalkContinue 50 }) 51 for _, h := range updateFoldHeadings { 52 children := foldHeadings[h.ID] 53 for i := len(children) - 1; 0 <= i; i-- { 54 h.Next.InsertAfter(children[i]) // Next 是 Block IAL 55 } 56 } 57 return 58} 59 60func IsInFoldedHeading(node, currentHeading *ast.Node) bool { 61 if nil == node { 62 return false 63 } 64 65 heading := HeadingParent(node) 66 if nil == heading { 67 return false 68 } 69 if "1" == heading.IALAttr("heading-fold") || "1" == heading.IALAttr("fold") { 70 return true 71 } 72 if heading == currentHeading { 73 // node 就在当前标题层级下的话不递归继续查询,直接返回不折叠 74 return false 75 } 76 return IsInFoldedHeading(heading, currentHeading) 77} 78 79func GetHeadingFold(nodes []*ast.Node) (ret []*ast.Node) { 80 for _, n := range nodes { 81 if "1" == n.IALAttr("heading-fold") { 82 ret = append(ret, n) 83 } 84 } 85 return 86} 87 88func GetParentFoldedHeading(node *ast.Node) (parentFoldedHeading *ast.Node) { 89 if nil == node { 90 return 91 } 92 93 currentLevel := 7 94 if ast.NodeHeading == node.Type { 95 currentLevel = node.HeadingLevel 96 } 97 for n := node.Previous; nil != n; n = n.Previous { 98 if ast.NodeHeading != n.Type { 99 continue 100 } 101 102 if n.HeadingLevel >= currentLevel { 103 continue 104 } 105 currentLevel = n.HeadingLevel 106 107 if "1" == n.IALAttr("fold") { 108 if ast.NodeHeading != node.Type { 109 parentFoldedHeading = n 110 } 111 if n.HeadingLevel < node.HeadingLevel { 112 parentFoldedHeading = n 113 } 114 } 115 } 116 return 117} 118 119func HeadingChildren(heading *ast.Node) (ret []*ast.Node) { 120 start := heading.Next 121 if nil == start { 122 return 123 } 124 if ast.NodeKramdownBlockIAL == start.Type { 125 start = start.Next // 跳过 heading 的 IAL 126 } 127 128 currentLevel := heading.HeadingLevel 129 for n := start; nil != n; n = n.Next { 130 if ast.NodeHeading == n.Type { 131 if currentLevel >= n.HeadingLevel { 132 break 133 } 134 } 135 ret = append(ret, n) 136 } 137 return 138} 139 140func SuperBlockLastHeading(sb *ast.Node) *ast.Node { 141 headings := sb.ChildrenByType(ast.NodeHeading) 142 if 0 < len(headings) { 143 return headings[len(headings)-1] 144 } 145 return nil 146} 147 148func HeadingParent(node *ast.Node) *ast.Node { 149 if nil == node { 150 return nil 151 } 152 153 currentLevel := 16 154 if ast.NodeHeading == node.Type { 155 currentLevel = node.HeadingLevel 156 } 157 158 for n := node.Previous; nil != n; n = n.Previous { 159 if ast.NodeHeading == n.Type && n.HeadingLevel < currentLevel { 160 return n 161 } 162 } 163 return node.Parent 164} 165 166func HeadingLevel(node *ast.Node) int { 167 if nil == node { 168 return 0 169 } 170 171 for n := node; nil != n; n = n.Previous { 172 if ast.NodeHeading == n.Type { 173 return n.HeadingLevel 174 } 175 } 176 return 0 177} 178 179func TopHeadingLevel(tree *parse.Tree) (ret int) { 180 ret = 7 181 for n := tree.Root.FirstChild; nil != n; n = n.Next { 182 if ast.NodeHeading == n.Type { 183 if ret > n.HeadingLevel { 184 ret = n.HeadingLevel 185 } 186 } 187 } 188 if 7 == ret { // 没有出现过标题时 189 ret = 0 190 } 191 return 192}