Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Vanessa 2023-04-14 12:03:26 +08:00
commit c6b3083a24
13 changed files with 177 additions and 41 deletions

View File

@ -548,7 +548,7 @@
"kernelFault1": "Please check if the network connection and kernel process is normal", "kernelFault1": "Please check if the network connection and kernel process is normal",
"kernelFault2": "If the problem still occurs after restarting, please report it via <a href=\"https://github.com/siyuan-note/siyuan/issues\" target=\"_blank\">GitHub Issues</a>", "kernelFault2": "If the problem still occurs after restarting, please report it via <a href=\"https://github.com/siyuan-note/siyuan/issues\" target=\"_blank\">GitHub Issues</a>",
"fontSize": "Font Size", "fontSize": "Font Size",
"fontSizeTip": "The default font size is 16px, this setting only affects the font size display in the editor", "fontSizeTip": "The default font size is 16px, this setting affects the editor and exported PDF/HTML font size display",
"font1": "This setting only affects the font family display in the editor, choose <code class='fn__code'>Default</code> to use the theme&#039;s font family", "font1": "This setting only affects the font family display in the editor, choose <code class='fn__code'>Default</code> to use the theme&#039;s font family",
"newNameFile": "The name of the new subdocument is", "newNameFile": "The name of the new subdocument is",
"newNameSettingFile": "The name of the new document is", "newNameSettingFile": "The name of the new document is",

View File

@ -548,7 +548,7 @@
"kernelFault1": "Verifique si la conexión de red y los procesos del kernel son normales", "kernelFault1": "Verifique si la conexión de red y los procesos del kernel son normales",
"kernelFault2": "Si el problema sigue produciéndose después de reiniciar, comuníquelo a través de <a href=\"https://github.com/siyuan-note/siyuan/issues\" target=\"_blank\">Problemas en GitHub</a>", "kernelFault2": "Si el problema sigue produciéndose después de reiniciar, comuníquelo a través de <a href=\"https://github.com/siyuan-note/siyuan/issues\" target=\"_blank\">Problemas en GitHub</a>",
"fontSize": "Tamaño de la fuente", "fontSize": "Tamaño de la fuente",
"fontSizeTip": "El tamaño de la fuente por defecto es de 16px, este ajuste sólo afecta a la visualización del tamaño de la fuente en el editor", "fontSizeTip": "El tamaño de fuente predeterminado es 16px, esta configuración afecta el editor y la visualización del tamaño de fuente PDF/HTML exportado",
"font1": "Este ajuste sólo afecta a la visualización de la familia de fuentes en el editor, elija <code class='fn__code'>Por defecto</code> para utilizar la familia de fuentes del tema", "font1": "Este ajuste sólo afecta a la visualización de la familia de fuentes en el editor, elija <code class='fn__code'>Por defecto</code> para utilizar la familia de fuentes del tema",
"newNameFile": "El nombre del nuevo subdocumento es", "newNameFile": "El nombre del nuevo subdocumento es",
"newNameSettingFile": "El nombre del nuevo documento es", "newNameSettingFile": "El nombre del nuevo documento es",

View File

@ -548,7 +548,7 @@
"kernelFault1": "Veuillez vérifier si la connexion réseau et les processus du noyau sont normaux", "kernelFault1": "Veuillez vérifier si la connexion réseau et les processus du noyau sont normaux",
"kernelFault2": "Si le problème persiste après le redémarrage, veuillez le signaler via <a href=\"https://github.com/siyuan-note/siyuan/issues\" target=\"_blank\">GitHub Issues</a>", "kernelFault2": "Si le problème persiste après le redémarrage, veuillez le signaler via <a href=\"https://github.com/siyuan-note/siyuan/issues\" target=\"_blank\">GitHub Issues</a>",
"fontSize": "Taille de la police", "fontSize": "Taille de la police",
"fontSizeTip": "La taille de la police par défaut est de 16px, ce paramètre n'affecte que la taille de la police affichée dans l'éditeur.", "fontSizeTip": "La taille de police par défaut est de 16px, ce paramètre affecte l'éditeur et l'affichage de la taille de police PDF/HTML exportée",
"font1": "Ce paramètre n'affecte que l'affichage de la famille de polices dans l'éditeur, choisissez <code class='fn__code'>Default</code> pour utiliser la famille de polices du thème.", "font1": "Ce paramètre n'affecte que l'affichage de la famille de polices dans l'éditeur, choisissez <code class='fn__code'>Default</code> pour utiliser la famille de polices du thème.",
"newNameFile": "Le nom du nouveau sous-document est", "newNameFile": "Le nom du nouveau sous-document est",
"newNameSettingFile": "Le nom du nouveau document est", "newNameSettingFile": "Le nom du nouveau document est",

View File

@ -548,7 +548,7 @@
"kernelFault1": "請檢查網絡連接和內核進程是否正常", "kernelFault1": "請檢查網絡連接和內核進程是否正常",
"kernelFault2": "如果重啟後仍然出現該問題,請通過<a href=\"https://ld246.com/article/1649901726096\" target=\"_blank\">這裡回饋</a>", "kernelFault2": "如果重啟後仍然出現該問題,請通過<a href=\"https://ld246.com/article/1649901726096\" target=\"_blank\">這裡回饋</a>",
"fontSize": "字型大小", "fontSize": "字型大小",
"fontSizeTip": "字型大小預設為 16px該設置僅影響編輯器內字體大小顯示", "fontSizeTip": "字號默認為 16px該設置影響編輯器和導出 PDF/HTML 字體大小顯示",
"font1": "該設置僅影響編輯器內字體顯示,選擇 <code class='fn__code'>預設</code> 則使用主題自帶字體", "font1": "該設置僅影響編輯器內字體顯示,選擇 <code class='fn__code'>預設</code> 則使用主題自帶字體",
"newNameFile": "新建子文檔名為", "newNameFile": "新建子文檔名為",
"newNameSettingFile": "新建文檔名為", "newNameSettingFile": "新建文檔名為",

View File

@ -548,7 +548,7 @@
"kernelFault1": "请检查网络连接和内核进程是否正常", "kernelFault1": "请检查网络连接和内核进程是否正常",
"kernelFault2": "如果重启后仍然出现该问题,请通过<a href=\"https://ld246.com/article/1649901726096\" target=\"_blank\">这里反馈</a>", "kernelFault2": "如果重启后仍然出现该问题,请通过<a href=\"https://ld246.com/article/1649901726096\" target=\"_blank\">这里反馈</a>",
"fontSize": "字号", "fontSize": "字号",
"fontSizeTip": "字号默认为 16px该设置仅影响编辑器内字体大小显示", "fontSizeTip": "字号默认为 16px该设置影响编辑器和导出 PDF/HTML 字体大小显示",
"font1": "该设置仅影响编辑器内字体显示,选择 <code class='fn__code'>默认</code> 则使用主题自带字体", "font1": "该设置仅影响编辑器内字体显示,选择 <code class='fn__code'>默认</code> 则使用主题自带字体",
"newNameFile": "新建子文档名为", "newNameFile": "新建子文档名为",
"newNameSettingFile": "新建文档名为", "newNameSettingFile": "新建文档名为",

View File

@ -18,6 +18,7 @@ package api
import ( import (
"fmt" "fmt"
"math"
"net/http" "net/http"
"path" "path"
"regexp" "regexp"
@ -95,7 +96,7 @@ func heading2Doc(c *gin.Context) {
name := path.Base(targetPath) name := path.Base(targetPath)
box := model.Conf.Box(targetNotebook) box := model.Conf.Box(targetNotebook)
files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), model.Conf.FileTree.Sort) files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), model.Conf.FileTree.Sort, false, model.Conf.FileTree.MaxListCount)
evt := util.NewCmdResult("heading2doc", 0, util.PushModeBroadcast) evt := util.NewCmdResult("heading2doc", 0, util.PushModeBroadcast)
evt.Data = map[string]interface{}{ evt.Data = map[string]interface{}{
"box": box, "box": box,
@ -140,7 +141,7 @@ func li2Doc(c *gin.Context) {
name := path.Base(targetPath) name := path.Base(targetPath)
box := model.Conf.Box(targetNotebook) box := model.Conf.Box(targetNotebook)
files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), model.Conf.FileTree.Sort) files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), model.Conf.FileTree.Sort, false, model.Conf.FileTree.MaxListCount)
evt := util.NewCmdResult("li2doc", 0, util.PushModeBroadcast) evt := util.NewCmdResult("li2doc", 0, util.PushModeBroadcast)
evt.Data = map[string]interface{}{ evt.Data = map[string]interface{}{
"box": box, "box": box,
@ -448,7 +449,7 @@ func createDailyNote(c *gin.Context) {
evt.AppId = app evt.AppId = app
name := path.Base(p) name := path.Base(p)
files, _, _ := model.ListDocTree(box.ID, path.Dir(p), model.Conf.FileTree.Sort) files, _, _ := model.ListDocTree(box.ID, path.Dir(p), model.Conf.FileTree.Sort, false, model.Conf.FileTree.MaxListCount)
evt.Data = map[string]interface{}{ evt.Data = map[string]interface{}{
"box": box, "box": box,
"path": p, "path": p,
@ -590,8 +591,13 @@ func searchDocs(c *gin.Context) {
return return
} }
flashcard := false
if arg["flashcard"] != nil {
flashcard = arg["flashcard"].(bool)
}
k := arg["k"].(string) k := arg["k"].(string)
ret.Data = model.SearchDocsByKeyword(k) ret.Data = model.SearchDocsByKeyword(k, flashcard)
} }
func listDocsByPath(c *gin.Context) { func listDocsByPath(c *gin.Context) {
@ -610,13 +616,26 @@ func listDocsByPath(c *gin.Context) {
if nil != sortParam { if nil != sortParam {
sortMode = int(sortParam.(float64)) sortMode = int(sortParam.(float64))
} }
files, totals, err := model.ListDocTree(notebook, p, sortMode) flashcard := false
if arg["flashcard"] != nil {
flashcard = arg["flashcard"].(bool)
}
maxListCount := model.Conf.FileTree.MaxListCount
if arg["maxListCount"] != nil {
// API `listDocsByPath` add an optional parameter `maxListCount` https://github.com/siyuan-note/siyuan/issues/7993
maxListCount = int(arg["maxListCount"].(float64))
if 0 >= maxListCount {
maxListCount = math.MaxInt
}
}
files, totals, err := model.ListDocTree(notebook, p, sortMode, flashcard, maxListCount)
if nil != err { if nil != err {
ret.Code = -1 ret.Code = -1
ret.Msg = err.Error() ret.Msg = err.Error()
return return
} }
if model.Conf.FileTree.MaxListCount < totals { if maxListCount < totals {
util.PushMsg(fmt.Sprintf(model.Conf.Language(48), len(files)), 7000) util.PushMsg(fmt.Sprintf(model.Conf.Language(48), len(files)), 7000)
} }
@ -708,7 +727,7 @@ func getDoc(c *gin.Context) {
func pushCreate(box *model.Box, p, treeID string, arg map[string]interface{}) { func pushCreate(box *model.Box, p, treeID string, arg map[string]interface{}) {
evt := util.NewCmdResult("create", 0, util.PushModeBroadcast) evt := util.NewCmdResult("create", 0, util.PushModeBroadcast)
name := path.Base(p) name := path.Base(p)
files, _, _ := model.ListDocTree(box.ID, path.Dir(p), model.Conf.FileTree.Sort) files, _, _ := model.ListDocTree(box.ID, path.Dir(p), model.Conf.FileTree.Sort, false, model.Conf.FileTree.MaxListCount)
evt.Data = map[string]interface{}{ evt.Data = map[string]interface{}{
"box": box, "box": box,
"path": p, "path": p,

View File

@ -308,10 +308,26 @@ func lsNotebooks(c *gin.Context) {
ret := gulu.Ret.NewResult() ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret) defer c.JSON(http.StatusOK, ret)
notebooks, err := model.ListNotebooks() arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
flashcard := false
if arg["flashcard"] != nil {
flashcard = arg["flashcard"].(bool)
}
var notebooks []*model.Box
if flashcard {
notebooks = model.GetFlashcardNotebooks()
} else {
var err error
notebooks, err = model.ListNotebooks()
if nil != err { if nil != err {
return return
} }
}
ret.Data = map[string]interface{}{ ret.Data = map[string]interface{}{
"notebooks": notebooks, "notebooks": notebooks,

View File

@ -107,6 +107,8 @@ func NetImg2LocalAssets(rootID, originalURL string) (err error) {
} }
name := filepath.Base(u) name := filepath.Base(u)
name = util.FilterFileName(name)
name = util.TruncateLenFileName(name)
name = "net-img-" + name name = "net-img-" + name
name = util.AssetName(name) name = util.AssetName(name)
writePath := filepath.Join(assetsDirPath, name) writePath := filepath.Join(assetsDirPath, name)

View File

@ -110,7 +110,7 @@ func loadTreeNodes(box string, p string, level int) (ret []*ast.Node, err error)
} }
func buildBlockChildren(block *Block) (err error) { func buildBlockChildren(block *Block) (err error) {
files, _, err := ListDocTree(block.Box, block.Path, Conf.FileTree.Sort) files, _, err := ListDocTree(block.Box, block.Path, Conf.FileTree.Sort, false, Conf.FileTree.MaxListCount)
if nil != err { if nil != err {
return return
} }

View File

@ -141,9 +141,18 @@ func (box *Box) moveCorruptedData(filePath string) {
logging.LogWarnf("moved corrupted data file [%s] to [%s]", filePath, to) logging.LogWarnf("moved corrupted data file [%s] to [%s]", filePath, to)
} }
func SearchDocsByKeyword(keyword string) (ret []map[string]string) { func SearchDocsByKeyword(keyword string, flashcard bool) (ret []map[string]string) {
ret = []map[string]string{} ret = []map[string]string{}
var deckBlockIDs []string
if flashcard {
deck := Decks[builtinDeckID]
if nil != deck {
return
}
deckBlockIDs = deck.GetBlockIDs()
}
openedBoxes := Conf.GetOpenedBoxes() openedBoxes := Conf.GetOpenedBoxes()
boxes := map[string]*Box{} boxes := map[string]*Box{}
for _, box := range openedBoxes { for _, box := range openedBoxes {
@ -154,8 +163,14 @@ func SearchDocsByKeyword(keyword string) (ret []map[string]string) {
if "" != keyword { if "" != keyword {
for _, box := range boxes { for _, box := range boxes {
if strings.Contains(box.Name, keyword) { if strings.Contains(box.Name, keyword) {
if flashcard {
if isBoxContainFlashcard(box.ID, deckBlockIDs) {
ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon}) ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon})
} }
} else {
ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon})
}
}
} }
condition := "hpath LIKE '%" + keyword + "%'" condition := "hpath LIKE '%" + keyword + "%'"
@ -168,17 +183,29 @@ func SearchDocsByKeyword(keyword string) (ret []map[string]string) {
rootBlocks = sql.QueryRootBlockByCondition(condition) rootBlocks = sql.QueryRootBlockByCondition(condition)
} else { } else {
for _, box := range boxes { for _, box := range boxes {
if flashcard {
if isBoxContainFlashcard(box.ID, deckBlockIDs) {
ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon})
}
} else {
ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon}) ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon})
} }
} }
}
for _, block := range rootBlocks { for _, rootBlock := range rootBlocks {
b := boxes[block.Box] b := boxes[rootBlock.Box]
if nil == b { if nil == b {
continue continue
} }
hPath := b.Name + block.HPath hPath := b.Name + rootBlock.HPath
ret = append(ret, map[string]string{"path": block.Path, "hPath": hPath, "box": block.Box, "boxIcon": b.Icon}) if flashcard {
if isTreeContainFlashcard(rootBlock.ID, deckBlockIDs) {
ret = append(ret, map[string]string{"path": rootBlock.Path, "hPath": hPath, "box": rootBlock.Box, "boxIcon": b.Icon})
}
} else {
ret = append(ret, map[string]string{"path": rootBlock.Path, "hPath": hPath, "box": rootBlock.Box, "boxIcon": b.Icon})
}
} }
sort.Slice(ret, func(i, j int) bool { sort.Slice(ret, func(i, j int) bool {
@ -194,7 +221,7 @@ type FileInfo struct {
isdir bool isdir bool
} }
func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err error) { func ListDocTree(boxID, path string, sortMode int, flashcard bool, maxListCount int) (ret []*File, totals int, err error) {
//os.MkdirAll("pprof", 0755) //os.MkdirAll("pprof", 0755)
//cpuProfile, _ := os.Create("pprof/cpu_profile_list_doc_tree") //cpuProfile, _ := os.Create("pprof/cpu_profile_list_doc_tree")
//pprof.StartCPUProfile(cpuProfile) //pprof.StartCPUProfile(cpuProfile)
@ -202,6 +229,15 @@ func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err
ret = []*File{} ret = []*File{}
var deckBlockIDs []string
if flashcard {
deck := Decks[builtinDeckID]
if nil != deck {
return
}
deckBlockIDs = deck.GetBlockIDs()
}
box := Conf.Box(boxID) box := Conf.Box(boxID)
if nil == box { if nil == box {
return nil, 0, errors.New(Conf.Language(0)) return nil, 0, errors.New(Conf.Language(0))
@ -247,8 +283,16 @@ func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err
} }
} }
} }
if flashcard {
rootID := strings.TrimSuffix(filepath.Base(parentDocPath), ".sy")
if isTreeContainFlashcard(rootID, deckBlockIDs) {
docs = append(docs, doc) docs = append(docs, doc)
} }
} else {
docs = append(docs, doc)
}
}
continue continue
} }
@ -259,8 +303,15 @@ func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err
if ial := box.docIAL(file.path); nil != ial { if ial := box.docIAL(file.path); nil != ial {
doc := box.docFromFileInfo(file, ial) doc := box.docFromFileInfo(file, ial)
if flashcard {
rootID := strings.TrimSuffix(filepath.Base(file.path), ".sy")
if isTreeContainFlashcard(rootID, deckBlockIDs) {
docs = append(docs, doc) docs = append(docs, doc)
continue }
} else {
docs = append(docs, doc)
}
} }
} }
elapsed = time.Now().Sub(start).Milliseconds() elapsed = time.Now().Sub(start).Milliseconds()
@ -312,8 +363,8 @@ func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err
return fileTreeFiles[i].Sort < fileTreeFiles[j].Sort return fileTreeFiles[i].Sort < fileTreeFiles[j].Sort
}) })
ret = append(ret, fileTreeFiles...) ret = append(ret, fileTreeFiles...)
if Conf.FileTree.MaxListCount < len(ret) { if maxListCount < len(ret) {
ret = ret[:Conf.FileTree.MaxListCount] ret = ret[:maxListCount]
} }
ret = ret[:] ret = ret[:]
return return
@ -339,8 +390,8 @@ func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err
ret = append(ret, docs...) ret = append(ret, docs...)
} }
if Conf.FileTree.MaxListCount < len(ret) { if maxListCount < len(ret) {
ret = ret[:Conf.FileTree.MaxListCount] ret = ret[:maxListCount]
} }
ret = ret[:] ret = ret[:]

View File

@ -38,8 +38,60 @@ import (
"github.com/siyuan-note/siyuan/kernel/util" "github.com/siyuan-note/siyuan/kernel/util"
) )
var Decks = map[string]*riff.Deck{} func GetFlashcardNotebooks() (ret []*Box) {
var deckLock = sync.Mutex{} deck := Decks[builtinDeckID]
if nil == deck {
return
}
deckBlockIDs := deck.GetBlockIDs()
boxes := Conf.GetOpenedBoxes()
for _, box := range boxes {
if isBoxContainFlashcard(box.ID, deckBlockIDs) {
ret = append(ret, box)
}
}
return
}
func isTreeContainFlashcard(rootID string, deckBlockIDs []string) (ret bool) {
blockIDs := getTreeSubTreeChildBlocks(rootID)
for _, blockID := range deckBlockIDs {
if gulu.Str.Contains(blockID, blockIDs) {
return true
}
}
return
}
func isBoxContainFlashcard(boxID string, deckBlockIDs []string) (ret bool) {
entries, err := os.ReadDir(filepath.Join(util.DataDir, boxID))
if nil != err {
logging.LogErrorf("read dir failed: %s", err)
return
}
for _, entry := range entries {
if entry.IsDir() {
continue
}
if !strings.HasSuffix(entry.Name(), ".sy") {
continue
}
rootID := strings.TrimSuffix(entry.Name(), ".sy")
if isTreeContainFlashcard(rootID, deckBlockIDs) {
return true
}
}
return
}
var (
Decks = map[string]*riff.Deck{}
deckLock = sync.Mutex{}
)
func GetNotebookFlashcards(boxID string, page int) (blocks []*Block, total, pageCount int) { func GetNotebookFlashcards(boxID string, page int) (blocks []*Block, total, pageCount int) {
blocks = []*Block{} blocks = []*Block{}
@ -66,9 +118,7 @@ func GetNotebookFlashcards(boxID string, page int) (blocks []*Block, total, page
var treeBlockIDs []string var treeBlockIDs []string
for _, rootID := range rootIDs { for _, rootID := range rootIDs {
blockIDs := getTreeSubTreeChildBlocks(rootID) blockIDs := getTreeSubTreeChildBlocks(rootID)
for _, blockID := range blockIDs { treeBlockIDs = append(treeBlockIDs, blockIDs...)
treeBlockIDs = append(treeBlockIDs, blockID)
}
} }
treeBlockIDs = gulu.Str.RemoveDuplicatedElem(treeBlockIDs) treeBlockIDs = gulu.Str.RemoveDuplicatedElem(treeBlockIDs)
@ -307,9 +357,7 @@ func GetNotebookDueFlashcards(boxID string, reviewedCardIDs []string) (ret []*Fl
var treeBlockIDs []string var treeBlockIDs []string
for _, rootID := range rootIDs { for _, rootID := range rootIDs {
blockIDs := getTreeSubTreeChildBlocks(rootID) blockIDs := getTreeSubTreeChildBlocks(rootID)
for _, blockID := range blockIDs { treeBlockIDs = append(treeBlockIDs, blockIDs...)
treeBlockIDs = append(treeBlockIDs, blockID)
}
} }
treeBlockIDs = gulu.Str.RemoveDuplicatedElem(treeBlockIDs) treeBlockIDs = gulu.Str.RemoveDuplicatedElem(treeBlockIDs)

View File

@ -194,7 +194,7 @@ func Mount(boxID string) (alreadyMount bool, err error) {
box.Index() box.Index()
// 缓存根一级的文档树展开 // 缓存根一级的文档树展开
ListDocTree(box.ID, "/", Conf.FileTree.Sort) ListDocTree(box.ID, "/", Conf.FileTree.Sort, false, Conf.FileTree.MaxListCount)
treenode.SaveBlockTree(false) treenode.SaveBlockTree(false)
util.ClearPushProgress(100) util.ClearPushProgress(100)

View File

@ -50,8 +50,8 @@ func MoveFoldHeading(updateNode, oldNode *ast.Node) {
}) })
for _, h := range updateFoldHeadings { for _, h := range updateFoldHeadings {
children := foldHeadings[h.ID] children := foldHeadings[h.ID]
for _, c := range children { for i := len(children) - 1; 0 <= i; i-- {
h.Next.InsertAfter(c) // Next 是 Block IAL h.Next.InsertAfter(children[i]) // Next 是 Block IAL
} }
} }
return return