diff --git a/kernel/model/box.go b/kernel/model/box.go index 4f7ad2f3c..066b4a4de 100644 --- a/kernel/model/box.go +++ b/kernel/model/box.go @@ -34,6 +34,7 @@ import ( "github.com/88250/lute/html" "github.com/88250/lute/lex" "github.com/88250/lute/parse" + "github.com/araddon/dateparse" "github.com/siyuan-note/filelock" "github.com/siyuan-note/logging" "github.com/siyuan-note/siyuan/kernel/cache" @@ -493,7 +494,7 @@ func parseKTree(kramdown []byte) (ret *parse.Tree) { return } -func normalizeTree(tree *parse.Tree) { +func normalizeTree(tree *parse.Tree) (yfmRootID, yfmTitle, yfmUpdated string) { if nil == tree.Root.FirstChild { tree.Root.AppendChild(treenode.NewParagraph()) } @@ -566,22 +567,64 @@ func normalizeTree(tree *parse.Tree) { parseErr := yaml.Unmarshal(n.Tokens, &attrs) if parseErr != nil { logging.LogWarnf("parse YAML front matter [%s] failed: %s", n.Tokens, parseErr) - } else { - for attrK, attrV := range attrs { - validKeyName := true - for i := 0; i < len(attrK); i++ { - if !lex.IsASCIILetterNumHyphen(attrK[i]) { - validKeyName = false - break + return ast.WalkContinue + } + + for attrK, attrV := range attrs { + // Improve parsing of YAML Front Matter when importing Markdown https://github.com/siyuan-note/siyuan/issues/12962 + if "title" == attrK { + yfmTitle = fmt.Sprint(attrV) + tree.Root.SetIALAttr("title", yfmTitle) + continue + } + if "date" == attrK { + created, parseTimeErr := dateparse.ParseIn(fmt.Sprint(attrV), time.Local) + if nil == parseTimeErr { + yfmRootID = created.Format("20060102150405") + "-" + gulu.Rand.String(7) + tree.Root.ID = yfmRootID + tree.Root.SetIALAttr("id", yfmRootID) + } + continue + } + if "lastmod" == attrK { + updated, parseTimeErr := dateparse.ParseIn(fmt.Sprint(attrV), time.Local) + if nil == parseTimeErr { + yfmUpdated = updated.Format("20060102150405") + tree.Root.SetIALAttr("updated", yfmUpdated) + } + continue + } + if "tags" == attrK { + var tags string + for i, tag := range attrV.([]any) { + tagStr := strings.TrimSpace(tag.(string)) + if "" == tag { + continue + } + tagStr = strings.TrimLeft(tagStr, "#,'\"") + tagStr = strings.TrimRight(tagStr, "#,'\"") + tags += tagStr + if i < len(attrV.([]any))-1 { + tags += "," } } - if !validKeyName { - logging.LogWarnf("invalid YAML key [%s] in [%s]", attrK, n.ID) - continue - } - - tree.Root.SetIALAttr("custom-"+attrK, fmt.Sprint(attrV)) + tree.Root.SetIALAttr("tags", tags) + continue } + + validKeyName := true + for i := 0; i < len(attrK); i++ { + if !lex.IsASCIILetterNumHyphen(attrK[i]) { + validKeyName = false + break + } + } + if !validKeyName { + logging.LogWarnf("invalid YAML key [%s] in [%s]", attrK, n.ID) + continue + } + + tree.Root.SetIALAttr("custom-"+attrK, fmt.Sprint(attrV)) } } diff --git a/kernel/model/export.go b/kernel/model/export.go index f9e7f57f7..bbaef5aee 100644 --- a/kernel/model/export.go +++ b/kernel/model/export.go @@ -1434,8 +1434,9 @@ func BatchExportMarkdown(boxID, folderPath string) (zipPath string) { func yfm(docIAL map[string]string) string { // 导出 Markdown 文件时开头附上一些元数据 https://github.com/siyuan-note/siyuan/issues/6880 - // 导出 Markdown 时在文档头添加 YFM 开关https://github.com/siyuan-note/siyuan/issues/7727 + if !Conf.Export.MarkdownYFM { + // 导出 Markdown 时在文档头添加 YFM 开关 https://github.com/siyuan-note/siyuan/issues/7727 return "" } diff --git a/kernel/model/import.go b/kernel/model/import.go index 14ab361b4..63e8575a2 100644 --- a/kernel/model/import.go +++ b/kernel/model/import.go @@ -22,24 +22,21 @@ import ( "encoding/json" "errors" "fmt" - "github.com/88250/lute" "image" "image/jpeg" "image/png" "io" "io/fs" - "math/rand" "os" "path" "path/filepath" "regexp" "runtime/debug" "sort" - "strconv" "strings" - "time" "github.com/88250/gulu" + "github.com/88250/lute" "github.com/88250/lute/ast" "github.com/88250/lute/html" "github.com/88250/lute/html/atom" @@ -759,12 +756,23 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) { return io.EOF } - tree = parseStdMd(data) + tree, yfmRootID, yfmTitle, yfmUpdated := parseStdMd(data) if nil == tree { logging.LogErrorf("parse tree [%s] failed", currentPath) return nil } + if "" != yfmRootID { + id = yfmRootID + } + if "" != yfmTitle { + title = yfmTitle + } + updated := yfmUpdated + fname := path.Base(targetPath) + targetPath = strings.ReplaceAll(targetPath, fname, id+".sy") + targetPaths[curRelPath] = targetPath + tree.ID = id tree.Root.ID = id tree.Root.SetIALAttr("id", tree.Root.ID) @@ -841,7 +849,7 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) { return ast.WalkContinue }) - reassignIDUpdated(tree) + reassignIDUpdated(tree, id, updated) importTrees = append(importTrees, tree) hPathsIDs[tree.HPath] = tree.ID @@ -864,13 +872,23 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) { if err != nil { return err } - tree := parseStdMd(data) + tree, yfmRootID, yfmTitle, yfmUpdated := parseStdMd(data) if nil == tree { msg := fmt.Sprintf("parse tree [%s] failed", localPath) logging.LogErrorf(msg) return errors.New(msg) } + if "" != yfmRootID { + id = yfmRootID + } + if "" != yfmTitle { + title = yfmTitle + } + updated := yfmUpdated + fname := path.Base(targetPath) + targetPath = strings.ReplaceAll(targetPath, fname, id+".sy") + tree.ID = id tree.Root.ID = id tree.Root.SetIALAttr("id", tree.Root.ID) @@ -937,7 +955,7 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) { return ast.WalkContinue }) - reassignIDUpdated(tree) + reassignIDUpdated(tree, id, updated) importTrees = append(importTrees, tree) } @@ -974,14 +992,14 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) { return } -func parseStdMd(markdown []byte) (ret *parse.Tree) { +func parseStdMd(markdown []byte) (ret *parse.Tree, yfmRootID, yfmTitle, yfmUpdated string) { luteEngine := util.NewStdLute() luteEngine.SetYamlFrontMatter(true) // 解析 YAML Front Matter https://github.com/siyuan-note/siyuan/issues/10878 ret = parse.Parse("", markdown, luteEngine.ParseOptions) if nil == ret { return } - normalizeTree(ret) + yfmRootID, yfmTitle, yfmUpdated = normalizeTree(ret) imgHtmlBlock2InlineImg(ret) parse.NestedInlines2FlattedSpansHybrid(ret, false) return @@ -1120,7 +1138,7 @@ func imgHtmlBlock2InlineImg(tree *parse.Tree) { return } -func reassignIDUpdated(tree *parse.Tree) { +func reassignIDUpdated(tree *parse.Tree, rootID, updated string) { var blockCount int ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus { if !entering || "" == n.ID { @@ -1131,23 +1149,22 @@ func reassignIDUpdated(tree *parse.Tree) { return ast.WalkContinue }) - ids := make([]string, blockCount) - min, _ := strconv.ParseInt(time.Now().Add(-1*time.Duration(blockCount)*time.Second).Format("20060102150405"), 10, 64) - for i := 0; i < blockCount; i++ { - ids[i] = newID(fmt.Sprintf("%d", min)) - min++ - } - - var i int ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus { if !entering || "" == n.ID { return ast.WalkContinue } - n.ID = ids[i] + n.ID = ast.NewNodeID() + if ast.NodeDocument == n.Type && "" != rootID { + n.ID = rootID + } + n.SetIALAttr("id", n.ID) - n.SetIALAttr("updated", util.TimeFromID(n.ID)) - i++ + if "" != updated { + n.SetIALAttr("updated", updated) + } else { + n.SetIALAttr("updated", util.TimeFromID(n.ID)) + } return ast.WalkContinue }) tree.ID = tree.Root.ID @@ -1155,19 +1172,6 @@ func reassignIDUpdated(tree *parse.Tree) { tree.Root.SetIALAttr("id", tree.Root.ID) } -func newID(t string) string { - return t + "-" + randStr(7) -} - -func randStr(length int) string { - letter := []rune("abcdefghijklmnopqrstuvwxyz0123456789") - b := make([]rune, length) - for i := range b { - b[i] = letter[rand.Intn(len(letter))] - } - return string(b) -} - func domAttrValue(n *html.Node, attrName string) string { if nil == n { return ""