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 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}