mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-09-22 08:30:42 +02:00
Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
a9f5cac024
8 changed files with 389 additions and 196 deletions
|
@ -277,8 +277,9 @@ export class Preview {
|
||||||
this.processZHTable(copyElement);
|
this.processZHTable(copyElement);
|
||||||
} else if (type === "yuque") {
|
} else if (type === "yuque") {
|
||||||
fetchPost("/api/lute/copyStdMarkdown", {
|
fetchPost("/api/lute/copyStdMarkdown", {
|
||||||
id: protyle.block.rootID,
|
id: protyle.block.id || protyle.options.blockId || protyle.block.parentID,
|
||||||
assetsDestSpace2Underscore: true,
|
assetsDestSpace2Underscore: true,
|
||||||
|
adjustHeadingLevel: true,
|
||||||
}, (response) => {
|
}, (response) => {
|
||||||
writeText(response.data);
|
writeText(response.data);
|
||||||
showMessage(`${window.siyuan.languages.pasteToYuque}`);
|
showMessage(`${window.siyuan.languages.pasteToYuque}`);
|
||||||
|
|
|
@ -4,10 +4,12 @@ import {transaction, updateTransaction} from "../wysiwyg/transaction";
|
||||||
import {getContenteditableElement} from "../wysiwyg/getBlock";
|
import {getContenteditableElement} from "../wysiwyg/getBlock";
|
||||||
import {
|
import {
|
||||||
fixTableRange,
|
fixTableRange,
|
||||||
focusBlock, focusByRange,
|
focusBlock,
|
||||||
|
focusByRange,
|
||||||
focusByWbr,
|
focusByWbr,
|
||||||
getEditorRange,
|
getEditorRange,
|
||||||
getSelectionOffset, setLastNodeRange,
|
getSelectionOffset,
|
||||||
|
setLastNodeRange,
|
||||||
} from "./selection";
|
} from "./selection";
|
||||||
import {Constants} from "../../constants";
|
import {Constants} from "../../constants";
|
||||||
import {highlightRender} from "../render/highlightRender";
|
import {highlightRender} from "../render/highlightRender";
|
||||||
|
@ -364,7 +366,6 @@ export const insertHTML = (html: string, protyle: IProtyle, isBlock = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
let innerHTML = unSpinHTML || // 在 table 中插入需要使用转换好的行内元素 https://github.com/siyuan-note/siyuan/issues/9358
|
let innerHTML = unSpinHTML || // 在 table 中插入需要使用转换好的行内元素 https://github.com/siyuan-note/siyuan/issues/9358
|
||||||
protyle.lute.SpinBlockDOM(html) || // 需要再 spin 一次 https://github.com/siyuan-note/siyuan/issues/7118
|
|
||||||
html; // 空格会被 Spin 不再,需要使用原文
|
html; // 空格会被 Spin 不再,需要使用原文
|
||||||
// 粘贴纯文本时会进行内部转义,这里需要进行反转义 https://github.com/siyuan-note/siyuan/issues/10620
|
// 粘贴纯文本时会进行内部转义,这里需要进行反转义 https://github.com/siyuan-note/siyuan/issues/10620
|
||||||
innerHTML = innerHTML.replace(/;;;lt;;;/g, "<").replace(/;;;gt;;;/g, ">");
|
innerHTML = innerHTML.replace(/;;;lt;;;/g, "<").replace(/;;;gt;;;/g, ">");
|
||||||
|
|
|
@ -48,7 +48,13 @@ func copyStdMarkdown(c *gin.Context) {
|
||||||
if nil != arg["assetsDestSpace2Underscore"] {
|
if nil != arg["assetsDestSpace2Underscore"] {
|
||||||
assetsDestSpace2Underscore = arg["assetsDestSpace2Underscore"].(bool)
|
assetsDestSpace2Underscore = arg["assetsDestSpace2Underscore"].(bool)
|
||||||
}
|
}
|
||||||
ret.Data = model.ExportStdMarkdown(id, assetsDestSpace2Underscore)
|
|
||||||
|
adjustHeadingLevel := false
|
||||||
|
if nil != arg["adjustHeadingLevel"] {
|
||||||
|
adjustHeadingLevel = arg["adjustHeadingLevel"].(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Data = model.ExportStdMarkdown(id, assetsDestSpace2Underscore, adjustHeadingLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
func html2BlockDOM(c *gin.Context) {
|
func html2BlockDOM(c *gin.Context) {
|
||||||
|
|
|
@ -343,7 +343,9 @@ func SearchAssetsByName(keyword string, exts []string) (ret []*cache.Asset) {
|
||||||
ret = []*cache.Asset{}
|
ret = []*cache.Asset{}
|
||||||
var keywords []string
|
var keywords []string
|
||||||
keywords = append(keywords, keyword)
|
keywords = append(keywords, keyword)
|
||||||
|
if "" != keyword {
|
||||||
keywords = append(keywords, strings.Split(keyword, " ")...)
|
keywords = append(keywords, strings.Split(keyword, " ")...)
|
||||||
|
}
|
||||||
pathHitCount := map[string]int{}
|
pathHitCount := map[string]int{}
|
||||||
filterByExt := 0 < len(exts)
|
filterByExt := 0 < len(exts)
|
||||||
for _, asset := range cache.GetAssets() {
|
for _, asset := range cache.GetAssets() {
|
||||||
|
|
|
@ -3820,6 +3820,7 @@ func updateAttributeViewColumn(operation *Operation) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
colType := av.KeyType(operation.Typ)
|
colType := av.KeyType(operation.Typ)
|
||||||
|
changeType := false
|
||||||
switch colType {
|
switch colType {
|
||||||
case av.KeyTypeBlock, av.KeyTypeText, av.KeyTypeNumber, av.KeyTypeDate, av.KeyTypeSelect, av.KeyTypeMSelect, av.KeyTypeURL, av.KeyTypeEmail,
|
case av.KeyTypeBlock, av.KeyTypeText, av.KeyTypeNumber, av.KeyTypeDate, av.KeyTypeSelect, av.KeyTypeMSelect, av.KeyTypeURL, av.KeyTypeEmail,
|
||||||
av.KeyTypePhone, av.KeyTypeMAsset, av.KeyTypeTemplate, av.KeyTypeCreated, av.KeyTypeUpdated, av.KeyTypeCheckbox,
|
av.KeyTypePhone, av.KeyTypeMAsset, av.KeyTypeTemplate, av.KeyTypeCreated, av.KeyTypeUpdated, av.KeyTypeCheckbox,
|
||||||
|
@ -3827,6 +3828,8 @@ func updateAttributeViewColumn(operation *Operation) (err error) {
|
||||||
for _, keyValues := range attrView.KeyValues {
|
for _, keyValues := range attrView.KeyValues {
|
||||||
if keyValues.Key.ID == operation.ID {
|
if keyValues.Key.ID == operation.ID {
|
||||||
keyValues.Key.Name = strings.TrimSpace(operation.Name)
|
keyValues.Key.Name = strings.TrimSpace(operation.Name)
|
||||||
|
|
||||||
|
changeType = keyValues.Key.Type != colType
|
||||||
keyValues.Key.Type = colType
|
keyValues.Key.Type = colType
|
||||||
|
|
||||||
for _, value := range keyValues.Values {
|
for _, value := range keyValues.Values {
|
||||||
|
@ -3838,6 +3841,12 @@ func updateAttributeViewColumn(operation *Operation) (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if changeType {
|
||||||
|
for _, view := range attrView.Views {
|
||||||
|
removeAttributeViewGroup0(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = av.SaveAttributeView(attrView)
|
err = av.SaveAttributeView(attrView)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -268,7 +268,7 @@ func Export2Liandi(id string) (err error) {
|
||||||
|
|
||||||
title := path.Base(tree.HPath)
|
title := path.Base(tree.HPath)
|
||||||
tags := tree.Root.IALAttr("tags")
|
tags := tree.Root.IALAttr("tags")
|
||||||
content := exportMarkdownContent0(tree, util.GetCloudForumAssetsServer()+time.Now().Format("2006/01")+"/siyuan/"+Conf.GetUser().UserId+"/", true,
|
content := exportMarkdownContent0(id, tree, util.GetCloudForumAssetsServer()+time.Now().Format("2006/01")+"/siyuan/"+Conf.GetUser().UserId+"/", true, false,
|
||||||
".md", 3, 1, 1,
|
".md", 3, 1, 1,
|
||||||
"#", "#",
|
"#", "#",
|
||||||
"", "",
|
"", "",
|
||||||
|
@ -1457,7 +1457,7 @@ func processPDFLinkEmbedAssets(pdfCtx *model.Context, assetDests []string, remov
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExportStdMarkdown(id string, assetsDestSpace2Underscore bool) string {
|
func ExportStdMarkdown(id string, assetsDestSpace2Underscore, adjustHeadingLevel bool) string {
|
||||||
tree, err := LoadTreeByBlockID(id)
|
tree, err := LoadTreeByBlockID(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.LogErrorf("load tree by block id [%s] failed: %s", id, err)
|
logging.LogErrorf("load tree by block id [%s] failed: %s", id, err)
|
||||||
|
@ -1495,7 +1495,7 @@ func ExportStdMarkdown(id string, assetsDestSpace2Underscore bool) string {
|
||||||
}
|
}
|
||||||
defBlockIDs = gulu.Str.RemoveDuplicatedElem(defBlockIDs)
|
defBlockIDs = gulu.Str.RemoveDuplicatedElem(defBlockIDs)
|
||||||
|
|
||||||
return exportMarkdownContent0(tree, cloudAssetsBase, assetsDestSpace2Underscore,
|
return exportMarkdownContent0(id, tree, cloudAssetsBase, assetsDestSpace2Underscore, adjustHeadingLevel,
|
||||||
".md", Conf.Export.BlockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
|
".md", Conf.Export.BlockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
|
||||||
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
|
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
|
||||||
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
|
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
|
||||||
|
@ -1989,7 +1989,7 @@ func ExportMarkdownContent(id string, refMode, embedMode int, addYfm, fillCSSVar
|
||||||
|
|
||||||
tree := prepareExportTree(bt)
|
tree := prepareExportTree(bt)
|
||||||
hPath = tree.HPath
|
hPath = tree.HPath
|
||||||
exportedMd = exportMarkdownContent0(tree, "", false,
|
exportedMd = exportMarkdownContent0(id, tree, "", false, false,
|
||||||
".md", refMode, embedMode, Conf.Export.FileAnnotationRefMode,
|
".md", refMode, embedMode, Conf.Export.FileAnnotationRefMode,
|
||||||
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
|
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
|
||||||
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
|
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
|
||||||
|
@ -2008,7 +2008,7 @@ func exportMarkdownContent(id, ext string, exportRefMode int, defBlockIDs []stri
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
isEmpty = nil == tree.Root.FirstChild.FirstChild
|
isEmpty = nil == tree.Root.FirstChild.FirstChild
|
||||||
exportedMd = exportMarkdownContent0(tree, "", false,
|
exportedMd = exportMarkdownContent0(id, tree, "", false, false,
|
||||||
ext, exportRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
|
ext, exportRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
|
||||||
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
|
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
|
||||||
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
|
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
|
||||||
|
@ -2021,7 +2021,7 @@ func exportMarkdownContent(id, ext string, exportRefMode int, defBlockIDs []stri
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportMarkdownContent0(tree *parse.Tree, cloudAssetsBase string, assetsDestSpace2Underscore bool,
|
func exportMarkdownContent0(id string, tree *parse.Tree, cloudAssetsBase string, assetsDestSpace2Underscore, adjustHeadingLv bool,
|
||||||
ext string, blockRefMode, blockEmbedMode, fileAnnotationRefMode int,
|
ext string, blockRefMode, blockEmbedMode, fileAnnotationRefMode int,
|
||||||
tagOpenMarker, tagCloseMarker string, blockRefTextLeft, blockRefTextRight string,
|
tagOpenMarker, tagCloseMarker string, blockRefTextLeft, blockRefTextRight string,
|
||||||
addTitle, inlineMemo bool, defBlockIDs []string, singleFile, fillCSSVar bool, treeCache *map[string]*parse.Tree) (ret string) {
|
addTitle, inlineMemo bool, defBlockIDs []string, singleFile, fillCSSVar bool, treeCache *map[string]*parse.Tree) (ret string) {
|
||||||
|
@ -2030,6 +2030,11 @@ func exportMarkdownContent0(tree *parse.Tree, cloudAssetsBase string, assetsDest
|
||||||
tagOpenMarker, tagCloseMarker,
|
tagOpenMarker, tagCloseMarker,
|
||||||
blockRefTextLeft, blockRefTextRight,
|
blockRefTextLeft, blockRefTextRight,
|
||||||
addTitle, inlineMemo, 0 < len(defBlockIDs), singleFile, treeCache)
|
addTitle, inlineMemo, 0 < len(defBlockIDs), singleFile, treeCache)
|
||||||
|
if adjustHeadingLv {
|
||||||
|
bt := treenode.GetBlockTree(id)
|
||||||
|
adjustHeadingLevel(bt, tree)
|
||||||
|
}
|
||||||
|
|
||||||
luteEngine := NewLute()
|
luteEngine := NewLute()
|
||||||
luteEngine.SetFootnotes(true)
|
luteEngine.SetFootnotes(true)
|
||||||
luteEngine.SetKramdownIAL(false)
|
luteEngine.SetKramdownIAL(false)
|
||||||
|
|
|
@ -764,6 +764,11 @@ func loadNodesByStartEnd(tree *parse.Tree, startID, endID string) (nodes []*ast.
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(nodes) >= Conf.Editor.DynamicLoadBlocks {
|
||||||
|
// 如果加载到指定数量的块则停止加载
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,6 +167,8 @@ func performTx(tx *Transaction) (ret *TxErr) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
isLargeInsert := tx.processLargeInsert()
|
||||||
|
if !isLargeInsert {
|
||||||
for _, op := range tx.DoOperations {
|
for _, op := range tx.DoOperations {
|
||||||
switch op.Action {
|
switch op.Action {
|
||||||
case "create":
|
case "create":
|
||||||
|
@ -316,6 +318,7 @@ func performTx(tx *Transaction) (ret *TxErr) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if cr := tx.commit(); nil != cr {
|
if cr := tx.commit(); nil != cr {
|
||||||
logging.LogErrorf("commit tx failed: %s", cr)
|
logging.LogErrorf("commit tx failed: %s", cr)
|
||||||
|
@ -324,6 +327,42 @@ func performTx(tx *Transaction) (ret *TxErr) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tx *Transaction) processLargeInsert() bool {
|
||||||
|
opSize := len(tx.DoOperations)
|
||||||
|
isLargeInsert := 128 < opSize
|
||||||
|
if isLargeInsert {
|
||||||
|
var previousID string
|
||||||
|
for i, op := range tx.DoOperations {
|
||||||
|
if i == opSize-1 {
|
||||||
|
if "delete" != op.Action {
|
||||||
|
// 最后一个是 delete
|
||||||
|
isLargeInsert = false
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if "insert" != op.Action {
|
||||||
|
isLargeInsert = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if "" == op.PreviousID {
|
||||||
|
isLargeInsert = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if "" == previousID {
|
||||||
|
previousID = op.PreviousID
|
||||||
|
} else if previousID != op.PreviousID {
|
||||||
|
isLargeInsert = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isLargeInsert {
|
||||||
|
tx.doLargeInsert(previousID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isLargeInsert
|
||||||
|
}
|
||||||
|
|
||||||
func (tx *Transaction) doMove(operation *Operation) (ret *TxErr) {
|
func (tx *Transaction) doMove(operation *Operation) (ret *TxErr) {
|
||||||
var err error
|
var err error
|
||||||
id := operation.ID
|
id := operation.ID
|
||||||
|
@ -784,8 +823,6 @@ func (tx *Transaction) doAppend(operation *Operation) (ret *TxErr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Transaction) doDelete(operation *Operation) (ret *TxErr) {
|
func (tx *Transaction) doDelete(operation *Operation) (ret *TxErr) {
|
||||||
// logging.LogInfof("commit delete [%+v]", operation)
|
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
id := operation.ID
|
id := operation.ID
|
||||||
tree, err := tx.loadTree(id)
|
tree, err := tx.loadTree(id)
|
||||||
|
@ -980,6 +1017,127 @@ func syncDelete2AttributeView(node *ast.Node) (changedAvIDs []string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tx *Transaction) doLargeInsert(previousID string) (ret *TxErr) {
|
||||||
|
tree, err := tx.loadTree(previousID)
|
||||||
|
if nil != err {
|
||||||
|
logging.LogErrorf("load tree [%s] failed: %s", previousID, err)
|
||||||
|
return &TxErr{code: TxErrCodeBlockNotFound, id: previousID}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, operation := range tx.DoOperations {
|
||||||
|
if "insert" != operation.Action {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
data := strings.ReplaceAll(operation.Data.(string), editor.FrontEndCaret, "")
|
||||||
|
subTree := tx.luteEngine.BlockDOM2Tree(data)
|
||||||
|
tx.processGlobalAssets(subTree)
|
||||||
|
|
||||||
|
insertedNode := subTree.Root.FirstChild
|
||||||
|
if nil == insertedNode {
|
||||||
|
return &TxErr{code: TxErrCodeBlockNotFound, msg: "invalid data tree", id: tree.ID}
|
||||||
|
}
|
||||||
|
var remains []*ast.Node
|
||||||
|
for remain := insertedNode.Next; nil != remain; remain = remain.Next {
|
||||||
|
if ast.NodeKramdownBlockIAL != remain.Type {
|
||||||
|
if "" == remain.ID {
|
||||||
|
remain.ID = ast.NewNodeID()
|
||||||
|
remain.SetIALAttr("id", remain.ID)
|
||||||
|
}
|
||||||
|
remains = append(remains, remain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if "" == insertedNode.ID {
|
||||||
|
insertedNode.ID = ast.NewNodeID()
|
||||||
|
insertedNode.SetIALAttr("id", insertedNode.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
node := treenode.GetNodeInTree(tree, previousID)
|
||||||
|
if nil == node {
|
||||||
|
logging.LogErrorf("get node [%s] in tree [%s] failed", previousID, tree.Root.ID)
|
||||||
|
return &TxErr{code: TxErrCodeBlockNotFound, id: previousID}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ast.NodeHeading == node.Type && "1" == node.IALAttr("fold") {
|
||||||
|
children := treenode.HeadingChildren(node)
|
||||||
|
if l := len(children); 0 < l {
|
||||||
|
node = children[l-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ast.NodeList == insertedNode.Type && nil != node.Parent && ast.NodeList == node.Parent.Type {
|
||||||
|
insertedNode = insertedNode.FirstChild
|
||||||
|
}
|
||||||
|
for i := len(remains) - 1; 0 <= i; i-- {
|
||||||
|
remain := remains[i]
|
||||||
|
node.InsertAfter(remain)
|
||||||
|
}
|
||||||
|
node.InsertAfter(insertedNode)
|
||||||
|
|
||||||
|
createdUpdated(insertedNode)
|
||||||
|
tx.nodes[insertedNode.ID] = insertedNode
|
||||||
|
tx.trees[tree.ID] = tree
|
||||||
|
|
||||||
|
// 收集引用的定义块 ID
|
||||||
|
refDefIDs := getRefDefIDs(insertedNode)
|
||||||
|
// 推送定义节点引用计数
|
||||||
|
for _, defID := range refDefIDs {
|
||||||
|
task.AppendAsyncTaskWithDelay(task.SetDefRefCount, util.SQLFlushInterval, refreshRefCount, defID)
|
||||||
|
}
|
||||||
|
|
||||||
|
upsertAvBlockRel(insertedNode)
|
||||||
|
|
||||||
|
// 复制为副本时将该副本块插入到数据库中 https://github.com/siyuan-note/siyuan/issues/11959
|
||||||
|
avs := insertedNode.IALAttr(av.NodeAttrNameAvs)
|
||||||
|
for _, avID := range strings.Split(avs, ",") {
|
||||||
|
if !ast.IsNodeIDPattern(avID) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
AddAttributeViewBlock(tx, []map[string]interface{}{{
|
||||||
|
"id": insertedNode.ID,
|
||||||
|
"isDetached": false,
|
||||||
|
}}, avID, "", previousID, false)
|
||||||
|
ReloadAttrView(avID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ast.NodeAttributeView == insertedNode.Type {
|
||||||
|
// 插入数据库块时需要重新绑定其中已经存在的块
|
||||||
|
// 比如剪切操作时,会先进行 delete 数据库解绑块,这里需要重新绑定 https://github.com/siyuan-note/siyuan/issues/13031
|
||||||
|
attrView, parseErr := av.ParseAttributeView(insertedNode.AttributeViewID)
|
||||||
|
if nil == parseErr {
|
||||||
|
trees, toBindNodes := tx.getAttrViewBoundNodes(attrView)
|
||||||
|
for _, toBindNode := range toBindNodes {
|
||||||
|
t := trees[toBindNode.ID]
|
||||||
|
bindBlockAv0(tx, insertedNode.AttributeViewID, toBindNode, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置视图 https://github.com/siyuan-note/siyuan/issues/15279
|
||||||
|
v := attrView.GetView(attrView.ViewID)
|
||||||
|
if nil != v {
|
||||||
|
insertedNode.AttributeViewType = string(v.LayoutType)
|
||||||
|
attrs := parse.IAL2Map(insertedNode.KramdownIAL)
|
||||||
|
if "" == attrs[av.NodeAttrView] {
|
||||||
|
attrs[av.NodeAttrView] = v.ID
|
||||||
|
err = setNodeAttrs(insertedNode, tree, attrs)
|
||||||
|
if err != nil {
|
||||||
|
logging.LogWarnf("set node [%s] attrs failed: %s", operation.BlockID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operation.ID = insertedNode.ID
|
||||||
|
operation.ParentID = insertedNode.Parent.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tx.writeTree(tree); nil != err {
|
||||||
|
return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: tree.ID}
|
||||||
|
}
|
||||||
|
return tx.doDelete(tx.DoOperations[len(tx.DoOperations)-1])
|
||||||
|
}
|
||||||
|
|
||||||
func (tx *Transaction) doInsert(operation *Operation) (ret *TxErr) {
|
func (tx *Transaction) doInsert(operation *Operation) (ret *TxErr) {
|
||||||
var bt *treenode.BlockTree
|
var bt *treenode.BlockTree
|
||||||
bts := treenode.GetBlockTrees([]string{operation.ParentID, operation.PreviousID, operation.NextID})
|
bts := treenode.GetBlockTrees([]string{operation.ParentID, operation.PreviousID, operation.NextID})
|
||||||
|
@ -1005,42 +1163,7 @@ func (tx *Transaction) doInsert(operation *Operation) (ret *TxErr) {
|
||||||
|
|
||||||
data := strings.ReplaceAll(operation.Data.(string), editor.FrontEndCaret, "")
|
data := strings.ReplaceAll(operation.Data.(string), editor.FrontEndCaret, "")
|
||||||
subTree := tx.luteEngine.BlockDOM2Tree(data)
|
subTree := tx.luteEngine.BlockDOM2Tree(data)
|
||||||
|
tx.processGlobalAssets(subTree)
|
||||||
if !tx.isGlobalAssetsInit {
|
|
||||||
tx.assetsDir = getAssetsDir(filepath.Join(util.DataDir, bt.BoxID), filepath.Dir(filepath.Join(util.DataDir, bt.BoxID, bt.Path)))
|
|
||||||
tx.isGlobalAssets = strings.HasPrefix(tx.assetsDir, filepath.Join(util.DataDir, "assets"))
|
|
||||||
tx.isGlobalAssetsInit = true
|
|
||||||
}
|
|
||||||
if !tx.isGlobalAssets {
|
|
||||||
// 本地资源文件需要移动到用户手动建立的 assets 下 https://github.com/siyuan-note/siyuan/issues/2410
|
|
||||||
ast.Walk(subTree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
|
||||||
if !entering {
|
|
||||||
return ast.WalkContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ast.NodeLinkDest == n.Type && bytes.HasPrefix(n.Tokens, []byte("assets/")) {
|
|
||||||
assetP := gulu.Str.FromBytes(n.Tokens)
|
|
||||||
assetPath, e := GetAssetAbsPath(assetP)
|
|
||||||
if nil != e {
|
|
||||||
logging.LogErrorf("get path of asset [%s] failed: %s", assetP, err)
|
|
||||||
return ast.WalkContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(assetPath, filepath.Join(util.DataDir, "assets")) {
|
|
||||||
// 非全局 assets 则跳过
|
|
||||||
return ast.WalkContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
// 只有全局 assets 才移动到相对 assets
|
|
||||||
targetP := filepath.Join(tx.assetsDir, filepath.Base(assetPath))
|
|
||||||
if e = filelock.Rename(assetPath, targetP); err != nil {
|
|
||||||
logging.LogErrorf("copy path of asset from [%s] to [%s] failed: %s", assetPath, targetP, err)
|
|
||||||
return ast.WalkContinue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ast.WalkContinue
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
insertedNode := subTree.Root.FirstChild
|
insertedNode := subTree.Root.FirstChild
|
||||||
if nil == insertedNode {
|
if nil == insertedNode {
|
||||||
|
@ -1189,6 +1312,47 @@ func (tx *Transaction) doInsert(operation *Operation) (ret *TxErr) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tx *Transaction) processGlobalAssets(tree *parse.Tree) {
|
||||||
|
if !tx.isGlobalAssetsInit {
|
||||||
|
tx.assetsDir = getAssetsDir(filepath.Join(util.DataDir, tree.Box), filepath.Dir(filepath.Join(util.DataDir, tree.Box, tree.Path)))
|
||||||
|
tx.isGlobalAssets = strings.HasPrefix(tx.assetsDir, filepath.Join(util.DataDir, "assets"))
|
||||||
|
tx.isGlobalAssetsInit = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if tx.isGlobalAssets {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 本地资源文件需要移动到用户手动建立的 assets 下 https://github.com/siyuan-note/siyuan/issues/2410
|
||||||
|
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||||
|
if !entering {
|
||||||
|
return ast.WalkContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ast.NodeLinkDest == n.Type && bytes.HasPrefix(n.Tokens, []byte("assets/")) {
|
||||||
|
assetP := gulu.Str.FromBytes(n.Tokens)
|
||||||
|
assetPath, e := GetAssetAbsPath(assetP)
|
||||||
|
if nil != e {
|
||||||
|
logging.LogErrorf("get path of asset [%s] failed: %s", assetP, e)
|
||||||
|
return ast.WalkContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(assetPath, filepath.Join(util.DataDir, "assets")) {
|
||||||
|
// 非全局 assets 则跳过
|
||||||
|
return ast.WalkContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有全局 assets 才移动到相对 assets
|
||||||
|
targetP := filepath.Join(tx.assetsDir, filepath.Base(assetPath))
|
||||||
|
if e = filelock.Rename(assetPath, targetP); e != nil {
|
||||||
|
logging.LogErrorf("copy path of asset from [%s] to [%s] failed: %s", assetPath, targetP, e)
|
||||||
|
return ast.WalkContinue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ast.WalkContinue
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (tx *Transaction) doUpdate(operation *Operation) (ret *TxErr) {
|
func (tx *Transaction) doUpdate(operation *Operation) (ret *TxErr) {
|
||||||
id := operation.ID
|
id := operation.ID
|
||||||
tree, err := tx.loadTree(id)
|
tree, err := tx.loadTree(id)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue