123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- // SiYuan - Refactor your thinking
- // Copyright (c) 2020-present, b3log.org
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Affero General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Affero General Public License for more details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
- package model
- import (
- "time"
- "github.com/88250/lute/ast"
- "github.com/88250/lute/parse"
- "github.com/emirpasic/gods/stacks/linkedliststack"
- "github.com/siyuan-note/siyuan/kernel/treenode"
- "github.com/siyuan-note/siyuan/kernel/util"
- )
- func (tx *Transaction) doMoveOutlineHeading(operation *Operation) (ret *TxErr) {
- headingID := operation.ID
- previousID := operation.PreviousID
- parentID := operation.ParentID
- tree, err := tx.loadTree(headingID)
- if nil != err {
- return &TxErr{code: TxErrCodeBlockNotFound, id: headingID}
- }
- operation.RetData = tree.Root.ID
- if headingID == parentID || headingID == previousID {
- return
- }
- heading := treenode.GetNodeInTree(tree, headingID)
- if nil == heading {
- return &TxErr{code: TxErrCodeBlockNotFound, id: headingID}
- }
- if ast.NodeDocument != heading.Parent.Type {
- // 仅支持文档根节点下第一层标题,不支持容器块内标题
- util.PushMsg(Conf.language(240), 5000)
- return
- }
- headings := []*ast.Node{}
- ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
- if entering && ast.NodeHeading == n.Type && !n.ParentIs(ast.NodeBlockquote) {
- headings = append(headings, n)
- }
- return ast.WalkContinue
- })
- headingChildren := treenode.HeadingChildren(heading)
- if "" != previousID {
- previousHeading := treenode.GetNodeInTree(tree, previousID)
- if nil == previousHeading {
- return &TxErr{code: TxErrCodeBlockNotFound, id: previousID}
- }
- if ast.NodeDocument != previousHeading.Parent.Type {
- // 仅支持文档根节点下第一层标题,不支持容器块内标题
- util.PushMsg(Conf.language(248), 5000)
- return
- }
- for _, h := range headingChildren {
- if h.ID == previousID {
- // 不能移动到自己的子标题下
- util.PushMsg(Conf.language(241), 5000)
- return
- }
- }
- generateOpTypeHistory(tree, HistoryOpOutline)
- targetNode := previousHeading
- previousHeadingChildren := treenode.HeadingChildren(previousHeading)
- if 0 < len(previousHeadingChildren) {
- targetNode = previousHeadingChildren[len(previousHeadingChildren)-1]
- }
- for _, h := range headingChildren {
- if h.ID == targetNode.ID {
- // 目标节点是当前标题的子节点,不需要移动
- return
- }
- }
- diffLevel := heading.HeadingLevel - previousHeading.HeadingLevel
- heading.HeadingLevel = previousHeading.HeadingLevel
- for i := len(headingChildren) - 1; i >= 0; i-- {
- child := headingChildren[i]
- if ast.NodeHeading == child.Type {
- child.HeadingLevel -= diffLevel
- if 6 < child.HeadingLevel {
- child.HeadingLevel = 6
- }
- }
- targetNode.InsertAfter(child)
- }
- targetNode.InsertAfter(heading)
- } else if "" != parentID {
- parentHeading := treenode.GetNodeInTree(tree, parentID)
- if nil == parentHeading {
- return &TxErr{code: TxErrCodeBlockNotFound, id: parentID}
- }
- if ast.NodeDocument != parentHeading.Parent.Type {
- // 仅支持文档根节点下第一层标题,不支持容器块内标题
- util.PushMsg(Conf.language(248), 5000)
- return
- }
- for _, h := range headingChildren {
- if h.ID == parentID {
- // 不能移动到自己的子标题下
- util.PushMsg(Conf.language(241), 5000)
- return
- }
- }
- generateOpTypeHistory(tree, HistoryOpOutline)
- targetNode := parentHeading
- parentHeadingChildren := treenode.HeadingChildren(parentHeading)
- // 找到下方第一个非标题节点
- var tmp []*ast.Node
- for _, child := range parentHeadingChildren {
- if ast.NodeHeading == child.Type {
- break
- }
- tmp = append(tmp, child)
- }
- parentHeadingChildren = tmp
- if 0 < len(parentHeadingChildren) {
- for _, child := range parentHeadingChildren {
- if child.ID == headingID {
- break
- }
- targetNode = child
- }
- }
- diffLevel := heading.HeadingLevel - parentHeading.HeadingLevel - 1
- heading.HeadingLevel = parentHeading.HeadingLevel + 1
- if 6 < heading.HeadingLevel {
- heading.HeadingLevel = 6
- }
- for i := len(headingChildren) - 1; i >= 0; i-- {
- child := headingChildren[i]
- if ast.NodeHeading == child.Type {
- child.HeadingLevel -= diffLevel
- if 6 < child.HeadingLevel {
- child.HeadingLevel = 6
- }
- }
- targetNode.InsertAfter(child)
- }
- targetNode.InsertAfter(heading)
- } else {
- generateOpTypeHistory(tree, HistoryOpOutline)
- // 移到第一个标题前
- var firstHeading *ast.Node
- for n := tree.Root.FirstChild; nil != n; n = n.Next {
- if ast.NodeHeading == n.Type {
- firstHeading = n
- break
- }
- }
- if nil == firstHeading || firstHeading.ID == heading.ID {
- return
- }
- diffLevel := heading.HeadingLevel - firstHeading.HeadingLevel
- heading.HeadingLevel = firstHeading.HeadingLevel
- firstHeading.InsertBefore(heading)
- for i := 0; i < len(headingChildren); i++ {
- child := headingChildren[i]
- if ast.NodeHeading == child.Type {
- child.HeadingLevel -= diffLevel
- if 6 < child.HeadingLevel {
- child.HeadingLevel = 6
- }
- }
- firstHeading.InsertBefore(child)
- }
- }
- if err = tx.writeTree(tree); nil != err {
- return
- }
- return
- }
- func Outline(rootID string) (ret []*Path, err error) {
- time.Sleep(util.FrontendQueueInterval)
- WaitForWritingFiles()
- ret = []*Path{}
- tree, _ := LoadTreeByBlockID(rootID)
- if nil == tree {
- return
- }
- ret = outline(tree)
- return
- }
- func outline(tree *parse.Tree) (ret []*Path) {
- luteEngine := NewLute()
- var headings []*Block
- ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
- if entering && ast.NodeHeading == n.Type && !n.ParentIs(ast.NodeBlockquote) {
- n.Box, n.Path = tree.Box, tree.Path
- block := &Block{
- RootID: tree.Root.ID,
- Depth: n.HeadingLevel,
- Box: n.Box,
- Path: n.Path,
- ID: n.ID,
- Content: renderOutline(n, luteEngine),
- Type: n.Type.String(),
- SubType: treenode.SubTypeAbbr(n),
- }
- headings = append(headings, block)
- return ast.WalkSkipChildren
- }
- return ast.WalkContinue
- })
- if 1 > len(headings) {
- return
- }
- var blocks []*Block
- stack := linkedliststack.New()
- for _, h := range headings {
- L:
- for ; ; stack.Pop() {
- cur, ok := stack.Peek()
- if !ok {
- blocks = append(blocks, h)
- stack.Push(h)
- break L
- }
- tip := cur.(*Block)
- if tip.Depth < h.Depth {
- tip.Children = append(tip.Children, h)
- stack.Push(h)
- break L
- }
- tip.Count = len(tip.Children)
- }
- }
- ret = toFlatTree(blocks, 0, "outline", tree)
- if 0 < len(ret) {
- children := ret[0].Blocks
- ret = nil
- for _, b := range children {
- resetDepth(b, 0)
- ret = append(ret, &Path{
- ID: b.ID,
- Box: b.Box,
- Name: b.Content,
- NodeType: b.Type,
- Type: "outline",
- SubType: b.SubType,
- Blocks: b.Children,
- Depth: 0,
- Count: b.Count,
- })
- }
- }
- return
- }
- func resetDepth(b *Block, depth int) {
- b.Depth = depth
- b.Count = len(b.Children)
- for _, c := range b.Children {
- resetDepth(c, depth+1)
- }
- }
|