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 "bytes"
21
22 "github.com/88250/lute/ast"
23 "github.com/88250/lute/editor"
24 "github.com/88250/lute/render"
25 "github.com/siyuan-note/logging"
26 "github.com/siyuan-note/siyuan/kernel/util"
27)
28
29func AutoSpace(rootID string) (err error) {
30 tree, err := LoadTreeByBlockID(rootID)
31 if err != nil {
32 return
33 }
34
35 logging.LogInfof("formatting tree [%s]...", rootID)
36 util.PushProtyleLoading(rootID, Conf.Language(116))
37 defer ReloadProtyle(rootID)
38
39 FlushTxQueue()
40
41 generateOpTypeHistory(tree, HistoryOpFormat)
42 luteEngine := NewLute()
43 ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
44 if !entering {
45 return ast.WalkContinue
46 }
47
48 switch n.Type {
49 case ast.NodeTextMark:
50 luteEngine.MergeSameTextMark(n) // 合并相邻的同类行级节点
51 case ast.NodeCodeBlockCode:
52 // 代码块中包含 ``` 时 `优化排版` 异常 `Optimize typography` exception when code block contains ``` https://github.com/siyuan-note/siyuan/issues/15843
53 n.Tokens = bytes.ReplaceAll(n.Tokens, []byte(editor.Zwj+"```"), []byte("```"))
54 n.Tokens = bytes.ReplaceAll(n.Tokens, []byte("```"), []byte(editor.Zwj+"```"))
55 }
56 return ast.WalkContinue
57 })
58
59 rootIAL := tree.Root.KramdownIAL
60 addBlockIALNodes(tree, false)
61
62 // 第一次格式化为了合并相邻的文本节点
63 formatRenderer := render.NewFormatRenderer(tree, luteEngine.RenderOptions)
64 md := formatRenderer.Render()
65 newTree := parseKTree(md)
66 newTree.Root.Spec = "1"
67 // 第二次格式化启用自动空格
68 luteEngine.SetAutoSpace(true)
69 formatRenderer = render.NewFormatRenderer(newTree, luteEngine.RenderOptions)
70 md = formatRenderer.Render()
71 newTree = parseKTree(md)
72 newTree.Root.Spec = "1"
73 newTree.Root.ID = tree.ID
74 newTree.Root.KramdownIAL = rootIAL
75 newTree.ID = tree.ID
76 newTree.Path = tree.Path
77 newTree.HPath = tree.HPath
78 newTree.Box = tree.Box
79 err = writeTreeUpsertQueue(newTree)
80 if err != nil {
81 return
82 }
83 logging.LogInfof("formatted tree [%s]", rootID)
84 util.RandomSleep(500, 700)
85 return
86}