diff --git a/kernel/api/block.go b/kernel/api/block.go index def886c0b..3d41e8d39 100644 --- a/kernel/api/block.go +++ b/kernel/api/block.go @@ -26,6 +26,7 @@ import ( "github.com/88250/lute/html" "github.com/gin-gonic/gin" "github.com/siyuan-note/logging" + "github.com/siyuan-note/siyuan/kernel/filesys" "github.com/siyuan-note/siyuan/kernel/model" "github.com/siyuan-note/siyuan/kernel/util" ) @@ -320,7 +321,7 @@ func getContentWordCount(c *gin.Context) { } content := arg["content"].(string) - ret.Data = model.ContentStat(content) + ret.Data = filesys.ContentStat(content) } func getBlocksWordCount(c *gin.Context) { @@ -337,7 +338,7 @@ func getBlocksWordCount(c *gin.Context) { for _, id := range idsArg { ids = append(ids, id.(string)) } - ret.Data = model.BlocksWordCount(ids) + ret.Data = filesys.BlocksWordCount(ids) } func getTreeStat(c *gin.Context) { @@ -350,7 +351,7 @@ func getTreeStat(c *gin.Context) { } id := arg["id"].(string) - ret.Data = model.StatTree(id) + ret.Data = filesys.StatTree(id) } func getDOMText(c *gin.Context) { diff --git a/kernel/filesys/stat.go b/kernel/filesys/stat.go new file mode 100644 index 000000000..c7538eb24 --- /dev/null +++ b/kernel/filesys/stat.go @@ -0,0 +1,223 @@ +// 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 . + +package filesys + +import ( + "bytes" + "github.com/88250/lute" + "github.com/88250/lute/ast" + "github.com/88250/lute/parse" + "github.com/siyuan-note/siyuan/kernel/av" + "github.com/siyuan-note/siyuan/kernel/treenode" + "github.com/siyuan-note/siyuan/kernel/util" +) + +func ContentStat(content string) (ret *util.BlockStatResult) { + luteEngine := util.NewLute() + return contentStat(content, luteEngine) +} + +func contentStat(content string, luteEngine *lute.Lute) (ret *util.BlockStatResult) { + tree := luteEngine.BlockDOM2Tree(content) + runeCnt, wordCnt, linkCnt, imgCnt, refCnt := tree.Root.Stat() + return &util.BlockStatResult{ + RuneCount: runeCnt, + WordCount: wordCnt, + LinkCount: linkCnt, + ImageCount: imgCnt, + RefCount: refCnt, + } +} + +func StatBlock(id string) (ret *util.BlockStatResult) { + trees := LoadTrees([]string{id}) + if 1 > len(trees) { + return + } + + tree := trees[id] + if nil == tree { + return + } + + node := treenode.GetNodeInTree(tree, id) + if nil == node { + return + } + + if ast.NodeDocument == node.Type { + return statTree(tree) + } + + runeCnt, wordCnt, linkCnt, imgCnt, refCnt := node.Stat() + ret = &util.BlockStatResult{ + runeCnt, + wordCnt, + linkCnt, + imgCnt, + refCnt, + 1, + } + return +} + +func StatTree(id string) (ret *util.BlockStatResult) { + trees := LoadTrees([]string{id}) + if 1 > len(trees) { + return + } + + tree := trees[id] + if nil == tree { + return + } + + return statTree(tree) +} + +func statTree(tree *parse.Tree) (ret *util.BlockStatResult) { + blockCount := 0 + var databaseBlockNodes []*ast.Node + ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus { + if !entering { + return ast.WalkContinue + } + + if n.IsBlock() { + blockCount++ + } + + if ast.NodeAttributeView != n.Type { + return ast.WalkContinue + } + + databaseBlockNodes = append(databaseBlockNodes, n) + return ast.WalkContinue + }) + + luteEngine := util.NewLute() + var dbRuneCnt, dbWordCnt, dbLinkCnt, dbImgCnt, dbRefCnt int + for _, n := range databaseBlockNodes { + if "" == n.AttributeViewID { + continue + } + + attrView, _ := av.ParseAttributeView(n.AttributeViewID) + if nil == attrView { + continue + } + + content := bytes.Buffer{} + for _, kValues := range attrView.KeyValues { + for _, v := range kValues.Values { + switch kValues.Key.Type { + case av.KeyTypeURL: + if v.IsEmpty() { + continue + } + + dbLinkCnt++ + content.WriteString(v.URL.Content) + case av.KeyTypeMAsset: + if v.IsEmpty() { + continue + } + + for _, asset := range v.MAsset { + if av.AssetTypeImage == asset.Type { + dbImgCnt++ + } + } + case av.KeyTypeBlock: + if v.IsEmpty() { + continue + } + + if !v.IsDetached { + dbRefCnt++ + } + content.WriteString(v.Block.Content) + case av.KeyTypeText: + if v.IsEmpty() { + continue + } + content.WriteString(v.Text.Content) + case av.KeyTypeNumber: + if v.IsEmpty() { + continue + } + v.Number.FormatNumber() + content.WriteString(v.Number.FormattedContent) + case av.KeyTypeEmail: + if v.IsEmpty() { + continue + } + content.WriteString(v.Email.Content) + case av.KeyTypePhone: + if v.IsEmpty() { + continue + } + content.WriteString(v.Phone.Content) + } + } + } + + dbStat := contentStat(content.String(), luteEngine) + dbRuneCnt += dbStat.RuneCount + dbWordCnt += dbStat.WordCount + } + + runeCnt, wordCnt, linkCnt, imgCnt, refCnt := tree.Root.Stat() + runeCnt += dbRuneCnt + wordCnt += dbWordCnt + linkCnt += dbLinkCnt + imgCnt += dbImgCnt + refCnt += dbRefCnt + return &util.BlockStatResult{ + RuneCount: runeCnt, + WordCount: wordCnt, + LinkCount: linkCnt, + ImageCount: imgCnt, + RefCount: refCnt, + BlockCount: blockCount, + } +} + +func BlocksWordCount(ids []string) (ret *util.BlockStatResult) { + ret = &util.BlockStatResult{} + trees := LoadTrees(ids) + for _, id := range ids { + tree := trees[id] + if nil == tree { + continue + } + + node := treenode.GetNodeInTree(tree, id) + if nil == node { + continue + } + + runeCnt, wordCnt, linkCnt, imgCnt, refCnt := node.Stat() + ret.RuneCount += runeCnt + ret.WordCount += wordCnt + ret.LinkCount += linkCnt + ret.ImageCount += imgCnt + ret.RefCount += refCnt + } + ret.BlockCount = len(ids) + return +} diff --git a/kernel/treenode/template.go b/kernel/filesys/template.go similarity index 95% rename from kernel/treenode/template.go rename to kernel/filesys/template.go index 0472a42f6..38654f39f 100644 --- a/kernel/treenode/template.go +++ b/kernel/filesys/template.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package treenode +package filesys import ( "math" @@ -25,6 +25,7 @@ import ( "github.com/Masterminds/sprig/v3" "github.com/araddon/dateparse" "github.com/siyuan-note/logging" + "github.com/siyuan-note/siyuan/kernel/treenode" "github.com/siyuan-note/siyuan/kernel/util" "github.com/spf13/cast" ) @@ -48,6 +49,7 @@ func BuiltInTemplateFuncs() (ret template.FuncMap) { ret["parseTime"] = parseTime ret["FormatFloat"] = FormatFloat ret["getHPathByID"] = getHPathByID + ret["statBlock"] = StatBlock return } @@ -73,7 +75,7 @@ func FormatFloat(format string, n float64) string { } func getHPathByID(id string) (ret string) { - bt := GetBlockTree(id) + bt := treenode.GetBlockTree(id) if nil == bt { return } diff --git a/kernel/model/file.go b/kernel/model/file.go index ce3dc59b4..d5b352348 100644 --- a/kernel/model/file.go +++ b/kernel/model/file.go @@ -17,7 +17,6 @@ package model import ( - "bytes" "errors" "fmt" "io/fs" @@ -442,163 +441,6 @@ func ListDocTree(boxID, listPath string, sortMode int, flashcard, showHidden boo return } -func ContentStat(content string) (ret *util.BlockStatResult) { - luteEngine := util.NewLute() - return contentStat(content, luteEngine) -} - -func contentStat(content string, luteEngine *lute.Lute) (ret *util.BlockStatResult) { - tree := luteEngine.BlockDOM2Tree(content) - runeCnt, wordCnt, linkCnt, imgCnt, refCnt := tree.Root.Stat() - return &util.BlockStatResult{ - RuneCount: runeCnt, - WordCount: wordCnt, - LinkCount: linkCnt, - ImageCount: imgCnt, - RefCount: refCnt, - } -} - -func BlocksWordCount(ids []string) (ret *util.BlockStatResult) { - ret = &util.BlockStatResult{} - trees := filesys.LoadTrees(ids) - for _, id := range ids { - tree := trees[id] - if nil == tree { - continue - } - - node := treenode.GetNodeInTree(tree, id) - if nil == node { - continue - } - - runeCnt, wordCnt, linkCnt, imgCnt, refCnt := node.Stat() - ret.RuneCount += runeCnt - ret.WordCount += wordCnt - ret.LinkCount += linkCnt - ret.ImageCount += imgCnt - ret.RefCount += refCnt - } - ret.BlockCount = len(ids) - return -} - -func StatTree(id string) (ret *util.BlockStatResult) { - FlushTxQueue() - - tree, _ := LoadTreeByBlockID(id) - if nil == tree { - return - } - - blockCount := 0 - var databaseBlockNodes []*ast.Node - ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus { - if !entering { - return ast.WalkContinue - } - - if n.IsBlock() { - blockCount++ - } - - if ast.NodeAttributeView != n.Type { - return ast.WalkContinue - } - - databaseBlockNodes = append(databaseBlockNodes, n) - return ast.WalkContinue - }) - - luteEngine := util.NewLute() - var dbRuneCnt, dbWordCnt, dbLinkCnt, dbImgCnt, dbRefCnt int - for _, n := range databaseBlockNodes { - if "" == n.AttributeViewID { - continue - } - - attrView, _ := av.ParseAttributeView(n.AttributeViewID) - if nil == attrView { - continue - } - - content := bytes.Buffer{} - for _, kValues := range attrView.KeyValues { - for _, v := range kValues.Values { - switch kValues.Key.Type { - case av.KeyTypeURL: - if v.IsEmpty() { - continue - } - - dbLinkCnt++ - content.WriteString(v.URL.Content) - case av.KeyTypeMAsset: - if v.IsEmpty() { - continue - } - - for _, asset := range v.MAsset { - if av.AssetTypeImage == asset.Type { - dbImgCnt++ - } - } - case av.KeyTypeBlock: - if v.IsEmpty() { - continue - } - - if !v.IsDetached { - dbRefCnt++ - } - content.WriteString(v.Block.Content) - case av.KeyTypeText: - if v.IsEmpty() { - continue - } - content.WriteString(v.Text.Content) - case av.KeyTypeNumber: - if v.IsEmpty() { - continue - } - v.Number.FormatNumber() - content.WriteString(v.Number.FormattedContent) - case av.KeyTypeEmail: - if v.IsEmpty() { - continue - } - content.WriteString(v.Email.Content) - case av.KeyTypePhone: - if v.IsEmpty() { - continue - } - content.WriteString(v.Phone.Content) - } - } - } - - dbStat := contentStat(content.String(), luteEngine) - dbRuneCnt += dbStat.RuneCount - dbWordCnt += dbStat.WordCount - } - - runeCnt, wordCnt, linkCnt, imgCnt, refCnt := tree.Root.Stat() - runeCnt += dbRuneCnt - wordCnt += dbWordCnt - linkCnt += dbLinkCnt - imgCnt += dbImgCnt - refCnt += dbRefCnt - return &util.BlockStatResult{ - RuneCount: runeCnt, - WordCount: wordCnt, - LinkCount: linkCnt, - ImageCount: imgCnt, - RefCount: refCnt, - BlockCount: blockCount, - } -} - func GetDoc(startID, endID, id string, index int, query string, queryTypes map[string]bool, queryMethod, mode int, size int, isBacklink, highlight bool) ( blockCount int, dom, parentID, parent2ID, rootID, typ string, eof, scroll bool, boxID, docPath string, isBacklinkExpand bool, keywords []string, err error) { //os.MkdirAll("pprof", 0755) diff --git a/kernel/model/template.go b/kernel/model/template.go index 56dedc417..93d15ad16 100644 --- a/kernel/model/template.go +++ b/kernel/model/template.go @@ -34,6 +34,7 @@ import ( "github.com/siyuan-note/filelock" "github.com/siyuan-note/logging" "github.com/siyuan-note/siyuan/kernel/av" + "github.com/siyuan-note/siyuan/kernel/filesys" "github.com/siyuan-note/siyuan/kernel/search" "github.com/siyuan-note/siyuan/kernel/sql" "github.com/siyuan-note/siyuan/kernel/treenode" @@ -43,7 +44,7 @@ import ( func RenderGoTemplate(templateContent string) (ret string, err error) { tmpl := template.New("") - tplFuncMap := treenode.BuiltInTemplateFuncs() + tplFuncMap := filesys.BuiltInTemplateFuncs() sql.SQLTemplateFuncs(&tplFuncMap) tmpl = tmpl.Funcs(tplFuncMap) tpl, err := tmpl.Parse(templateContent) @@ -254,7 +255,7 @@ func RenderDynamicIconContentTemplate(content, id string) (ret string) { dataModel["alias"] = block.Alias goTpl := template.New("").Delims(".action{", "}") - tplFuncMap := treenode.BuiltInTemplateFuncs() + tplFuncMap := filesys.BuiltInTemplateFuncs() sql.SQLTemplateFuncs(&tplFuncMap) goTpl = goTpl.Funcs(tplFuncMap) tpl, err := goTpl.Funcs(tplFuncMap).Parse(content) @@ -304,7 +305,7 @@ func RenderTemplate(p, id string, preview bool) (tree *parse.Tree, dom string, e } goTpl := template.New("").Delims(".action{", "}") - tplFuncMap := treenode.BuiltInTemplateFuncs() + tplFuncMap := filesys.BuiltInTemplateFuncs() sql.SQLTemplateFuncs(&tplFuncMap) goTpl = goTpl.Funcs(tplFuncMap) tpl, err := goTpl.Funcs(tplFuncMap).Parse(gulu.Str.FromBytes(md)) diff --git a/kernel/sql/av.go b/kernel/sql/av.go index aad44d1dd..16f1846fc 100644 --- a/kernel/sql/av.go +++ b/kernel/sql/av.go @@ -27,6 +27,7 @@ import ( "github.com/88250/lute/ast" "github.com/siyuan-note/logging" "github.com/siyuan-note/siyuan/kernel/av" + "github.com/siyuan-note/siyuan/kernel/filesys" "github.com/siyuan-note/siyuan/kernel/treenode" "github.com/siyuan-note/siyuan/kernel/util" ) @@ -420,7 +421,7 @@ func RenderTemplateCol(ial map[string]string, rowValues []*av.KeyValues, tplCont } goTpl := template.New("").Delims(".action{", "}") - tplFuncMap := treenode.BuiltInTemplateFuncs() + tplFuncMap := filesys.BuiltInTemplateFuncs() SQLTemplateFuncs(&tplFuncMap) goTpl = goTpl.Funcs(tplFuncMap) tpl, err := goTpl.Parse(tplContent)