2023-01-27 21:14:09 +08:00
// Lute - 一款结构化的 Markdown 引擎,支持 Go 和 JavaScript
// Copyright (c) 2019-present, b3log.org
//
// Lute is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
package filesys
import (
"bytes"
"strings"
"github.com/88250/lute/ast"
"github.com/88250/lute/editor"
"github.com/88250/lute/parse"
"github.com/88250/lute/util"
"github.com/goccy/go-json"
"github.com/siyuan-note/siyuan/kernel/treenode"
)
func ParseJSONWithoutFix ( jsonData [ ] byte , options * parse . Options ) ( ret * parse . Tree , err error ) {
root := & ast . Node { }
err = json . Unmarshal ( jsonData , root )
if nil != err {
return
}
ret = & parse . Tree { Name : "" , ID : root . ID , Root : & ast . Node { Type : ast . NodeDocument , ID : root . ID , Spec : root . Spec } , Context : & parse . Context { ParseOption : options } }
ret . Root . KramdownIAL = parse . Map2IAL ( root . Properties )
ret . Context . Tip = ret . Root
if nil == root . Children {
return
}
idMap := map [ string ] bool { }
for _ , child := range root . Children {
genTreeByJSON ( child , ret , & idMap , nil , nil , true )
}
return
}
func ParseJSON ( jsonData [ ] byte , options * parse . Options ) ( ret * parse . Tree , needFix bool , err error ) {
root := & ast . Node { }
err = json . Unmarshal ( jsonData , root )
if nil != err {
return
}
ret = & parse . Tree { Name : "" , ID : root . ID , Root : & ast . Node { Type : ast . NodeDocument , ID : root . ID , Spec : root . Spec } , Context : & parse . Context { ParseOption : options } }
ret . Root . KramdownIAL = parse . Map2IAL ( root . Properties )
for _ , kv := range ret . Root . KramdownIAL {
if strings . Contains ( kv [ 1 ] , "\n" ) {
val := kv [ 1 ]
val = strings . ReplaceAll ( val , "\n" , editor . IALValEscNewLine )
ret . Root . SetIALAttr ( kv [ 0 ] , val )
needFix = true
}
}
ret . Context . Tip = ret . Root
if nil == root . Children {
newPara := & ast . Node { Type : ast . NodeParagraph , ID : ast . NewNodeID ( ) }
newPara . SetIALAttr ( "id" , newPara . ID )
ret . Root . AppendChild ( newPara )
needFix = true
return
}
needMigrate2Spec1 := false
idMap := map [ string ] bool { }
for _ , child := range root . Children {
genTreeByJSON ( child , ret , & idMap , & needFix , & needMigrate2Spec1 , false )
}
if nil == ret . Root . FirstChild {
// 如果是空文档的话挂一个空段落上去
newP := treenode . NewParagraph ( )
ret . Root . AppendChild ( newP )
ret . Root . SetIALAttr ( "updated" , newP . ID [ : 14 ] )
}
if needMigrate2Spec1 {
parse . NestedInlines2FlattedSpans ( ret )
needFix = true
}
return
}
func genTreeByJSON ( node * ast . Node , tree * parse . Tree , idMap * map [ string ] bool , needFix , needMigrate2Spec1 * bool , ignoreFix bool ) {
node . Tokens , node . Type = util . StrToBytes ( node . Data ) , ast . Str2NodeType ( node . TypeStr )
node . Data , node . TypeStr = "" , ""
node . KramdownIAL = parse . Map2IAL ( node . Properties )
node . Properties = nil
if ! ignoreFix {
// 历史数据订正
if - 1 == node . Type {
* needFix = true
node . Type = ast . NodeParagraph
node . AppendChild ( & ast . Node { Type : ast . NodeText , Tokens : node . Tokens } )
node . Children = nil
}
switch node . Type {
case ast . NodeList :
if 1 > len ( node . Children ) {
* needFix = true
return // 忽略空列表
}
case ast . NodeListItem :
if 1 > len ( node . Children ) {
* needFix = true
return // 忽略空列表项
}
case ast . NodeBlockquote :
if 2 > len ( node . Children ) {
* needFix = true
return // 忽略空引述
}
case ast . NodeSuperBlock :
if 4 > len ( node . Children ) {
* needFix = true
return // 忽略空超级块
}
case ast . NodeMathBlock :
if 1 > len ( node . Children ) {
* needFix = true
return // 忽略空公式
}
case ast . NodeBlockQueryEmbed :
if 1 > len ( node . Children ) {
* needFix = true
return // 忽略空查询嵌入块
}
}
fixLegacyData ( tree . Context . Tip , node , idMap , needFix , needMigrate2Spec1 )
}
tree . Context . Tip . AppendChild ( node )
tree . Context . Tip = node
defer tree . Context . ParentTip ( )
if nil == node . Children {
return
}
for _ , child := range node . Children {
genTreeByJSON ( child , tree , idMap , needFix , needMigrate2Spec1 , ignoreFix )
}
node . Children = nil
}
func fixLegacyData ( tip , node * ast . Node , idMap * map [ string ] bool , needFix , needMigrate2Spec1 * bool ) {
if node . IsBlock ( ) {
if "" == node . ID {
node . ID = ast . NewNodeID ( )
node . SetIALAttr ( "id" , node . ID )
* needFix = true
}
if 0 < len ( node . Children ) && ast . NodeBr . String ( ) == node . Children [ len ( node . Children ) - 1 ] . TypeStr {
// 剔除块尾多余的软换行 https://github.com/siyuan-note/siyuan/issues/6191
node . Children = node . Children [ : len ( node . Children ) - 1 ]
* needFix = true
}
}
if "" != node . ID {
if _ , ok := ( * idMap ) [ node . ID ] ; ok {
node . ID = ast . NewNodeID ( )
node . SetIALAttr ( "id" , node . ID )
* needFix = true
}
( * idMap ) [ node . ID ] = true
}
switch node . Type {
case ast . NodeIFrame :
if bytes . Contains ( node . Tokens , util . StrToBytes ( "iframe-content" ) ) {
start := bytes . Index ( node . Tokens , util . StrToBytes ( "<iframe" ) )
end := bytes . Index ( node . Tokens , util . StrToBytes ( "</iframe>" ) )
node . Tokens = node . Tokens [ start : end + 9 ]
* needFix = true
}
case ast . NodeWidget :
if bytes . Contains ( node . Tokens , util . StrToBytes ( "http://127.0.0.1:6806" ) ) {
node . Tokens = bytes . ReplaceAll ( node . Tokens , [ ] byte ( "http://127.0.0.1:6806" ) , nil )
* needFix = true
}
case ast . NodeList :
if nil != node . ListData && 3 != node . ListData . Typ && 0 < len ( node . Children ) &&
nil != node . Children [ 0 ] . ListData && 3 == node . Children [ 0 ] . ListData . Typ {
node . ListData . Typ = 3
* needFix = true
}
case ast . NodeMark :
if 3 == len ( node . Children ) && "NodeText" == node . Children [ 1 ] . TypeStr {
if strings . HasPrefix ( node . Children [ 1 ] . Data , " " ) || strings . HasSuffix ( node . Children [ 1 ] . Data , " " ) {
node . Children [ 1 ] . Data = strings . TrimSpace ( node . Children [ 1 ] . Data )
* needFix = true
}
}
case ast . NodeHeading :
if 6 < node . HeadingLevel {
node . HeadingLevel = 6
* needFix = true
}
case ast . NodeLinkDest :
if bytes . HasPrefix ( node . Tokens , [ ] byte ( "assets/" ) ) && bytes . HasSuffix ( node . Tokens , [ ] byte ( " " ) ) {
node . Tokens = bytes . TrimSpace ( node . Tokens )
* needFix = true
}
case ast . NodeText :
if nil != tip . LastChild && ast . NodeTagOpenMarker == tip . LastChild . Type && 1 > len ( node . Tokens ) {
node . Tokens = [ ] byte ( "Untitled" )
* needFix = true
}
case ast . NodeTagCloseMarker :
if nil != tip . LastChild {
if ast . NodeTagOpenMarker == tip . LastChild . Type {
tip . AppendChild ( & ast . Node { Type : ast . NodeText , Tokens : [ ] byte ( "Untitled" ) } )
* needFix = true
} else if "" == tip . LastChild . Text ( ) {
tip . LastChild . Type = ast . NodeText
tip . LastChild . Tokens = [ ] byte ( "Untitled" )
* needFix = true
}
}
case ast . NodeBlockRef :
// 建立索引时无法解析 `v2.2.0-` 版本的块引用 https://github.com/siyuan-note/siyuan/issues/6889
// 早先的迁移程序有缺陷,漏迁移了块引用节点,这里检测到块引用节点后标识需要迁移
* needMigrate2Spec1 = true
2023-08-15 10:07:28 +08:00
case ast . NodeInlineHTML :
* needFix = true
node . Type = ast . NodeHTMLBlock
2023-01-27 21:14:09 +08:00
}
for _ , kv := range node . KramdownIAL {
if strings . Contains ( kv [ 1 ] , "\n" ) {
val := kv [ 1 ]
val = strings . ReplaceAll ( val , "\n" , editor . IALValEscNewLine )
node . SetIALAttr ( kv [ 0 ] , val )
* needFix = true
}
}
}