#help_index "DolDoc/Tree"

public Bool DocTreeFind(CDoc *haystack_doc, U8 *needle_path, CDocEntry **_tree_entry=NULL,
                        CDocEntry **_start_indent=NULL, CDocEntry **_end_indent=NULL)
{//Find tree widget start and end.
    I64          i = 0, k = 0;
    U8          *st1 = StrNew(needle_path), *st2 = MAlloc(StrLen(needle_path) + 1);
    Bool         res = FALSE, unlock_doc = DocLock(haystack_doc);
    CDocEntry   *doc_e = haystack_doc->head.next;

    if (_tree_entry)
        *_tree_entry = haystack_doc;
    if (_start_indent)
        *_start_indent = haystack_doc;
    if (_end_indent)
        *_end_indent = haystack_doc;

    while (*st1 && doc_e != haystack_doc)
    {
        StrFirstRemove(st1, "/", st2);
        if (*st2)
        {
            while (doc_e != haystack_doc)
            {
                if (doc_e->type_u8 == DOCT_INDENT)
                    i += doc_e->attr;
                else if (i == k && doc_e->de_flags & DOCEF_TREE && !StrCompare(doc_e->tag + 3, st2))
                {
                    if (*st1)
                        break;
                    else
                    {
                        if (_tree_entry)
                            *_tree_entry = doc_e;
                        i = 0;
                        while (doc_e != haystack_doc && doc_e->type_u8 != DOCT_INDENT)
                            doc_e = doc_e->next;
                        if (doc_e != haystack_doc)
                        {
                            i = doc_e->attr;
                            if (_start_indent)
                                *_start_indent = doc_e;
                            doc_e = doc_e->next;
                            while (doc_e != haystack_doc && i > 0)
                            {
                                if (doc_e->type_u8 == DOCT_INDENT)
                                {
                                    i += doc_e->attr;
                                    if (i <= 0)
                                    {
                                        if (_end_indent)
                                            *_end_indent = doc_e;
                                        res = TRUE;
                                        break;
                                    }
                                }
                                doc_e = doc_e->next;
                            }
                        }
                        goto ft_done;
                    }
                }
                doc_e = doc_e->next;
            }
            k += 2;
        }
    }
ft_done:
    if (unlock_doc)
        DocUnlock(haystack_doc);
    Free(st1);
    Free(st2);

    return res;
}

public Bool DocTreeFFind(U8 *name, U8 *path)
{//Find tree widget in file.
    CDoc *doc = DocRead(name);
    Bool  res = DocTreeFind(doc, path);

    DocDel(doc);

    return res;
}

public Bool DocTreeMake(CDoc *doc, U8 *path)
{//Make tree widget.
    I64          i = 0, j = I64_MIN, k = 0;
    U8          *st1 = StrNew(path), *st2 = MAlloc(StrLen(path) + 1), *st3 = StrNew(path);
    Bool         res = TRUE, unlock_doc = DocLock(doc);
    CDocEntry   *doc_e = doc->head.next;

    doc->cur_entry  = doc;
    doc->cur_col    = 0;
    while (*st1 && doc_e != doc)
    {
        StrFirstRemove(st1, "/", st2);
        if (*st2)
        {
            while (doc_e != doc)
            {
                if (doc_e->type_u8 == DOCT_INDENT)
                {
                    i += doc_e->attr;
                    if (i == j)
                    {
                        doc->cur_entry  = doc_e;
                        doc->cur_col    = 0;
                        goto mt_done;
                    }
                }
                else if (i == k && doc_e->de_flags & DOCEF_TREE && !StrCompare(doc_e->tag + 3, st2))
                {
                    Free(st3);
                    st3 = StrNew(st1);
                    j = i;
                    if (!*st1)
                        res = FALSE;
                    else
                        break;
                }
                doc_e = doc_e->next;
            }
            k += 2;
        }
    }
mt_done:
    if (res)
    {
        while (*st3)
        {
            StrFirstRemove(st3, "/", st2);
            if (*st2)
            {
                DocPrint(doc, "$TR+C,\"%s\"$\n$ID,2$", st2);
                doc->cur_entry  = DocPrint(doc, "$ID,-2$");
                doc->cur_col    = 0;
            }
        }
    }
    if (unlock_doc)
        DocUnlock(doc);
    Free(st1);
    Free(st2);
    Free(st3);

    return res;
}

Bool DocTreeWriteJoin(CDoc *doc, U8 *path, Bool write, U8 *format, I64 argc, I64 *argv)
{//Rewrite doc tree branch.
    CDocEntry   *tree_branch, *start_indent, *end_indent;
    U8          *buf = StrPrintJoin(NULL, format, argc, argv);
    Bool         res, unlock_doc = DocLock(doc);

    if (res = DocTreeFind(doc, path, &tree_branch, &start_indent, &end_indent))
    {
        DocCut(doc, start_indent->next, end_indent->last);
        doc->cur_entry  = start_indent->next;
        doc->cur_col    = doc->cur_entry->min_col;
    }
    else
        DocTreeMake(doc, path);
    DocPrint(doc, "%s", buf);
    if (write && DriveIsWritable(*doc->filename.name))
        DocWrite(doc);
    if (unlock_doc)
        DocUnlock(doc);
    Free(buf);

    return res;
}

Bool DocTreeAppendJoin(CDoc *doc, U8 *path, Bool write, U8 *format, I64 argc, I64 *argv)
{//Append to doc tree branch.
    CDocEntry   *tree_branch, *start_indent, *end_indent;
    U8          *buf = StrPrintJoin(NULL, format, argc, argv);
    Bool         res, unlock_doc = DocLock(doc);

    if (res = DocTreeFind(doc, path, &tree_branch, &start_indent, &end_indent))
    {
        doc->cur_entry  = end_indent;
        doc->cur_col    = doc->cur_entry->min_col;
    }
    else
        DocTreeMake(doc, path);
    DocPrint(doc, "%s", buf);
    if (write && DriveIsWritable(*doc->filename.name))
        DocWrite(doc);
    if (unlock_doc)
        DocUnlock(doc);
    Free(buf);

    return res;
}

public Bool DocTreeWrite(CDoc *doc, U8 *path, Bool write=TRUE, U8 *format, ...)
{//Rewrite doc tree branch.
    return DocTreeWriteJoin(doc, path, write, format, argc, argv);
}

public Bool DocTreeAppend(CDoc *doc, U8 *path, Bool write=TRUE, U8 *format, ...)
{//Append to doc tree branch.
    return DocTreeAppendJoin(doc, path, write, format, argc, argv);
}

public Bool DocTreeFWrite(U8 *name, U8 *path, U8 *format, ...)
{//Rewrite doc tree branch in file.
    CDoc *doc = DocRead(name);
    Bool  res = DocTreeWriteJoin(doc, path, TRUE, format, argc, argv);

    DocDel(doc);

    return res;
}

public Bool DocTreeFAppend(U8 *name, U8 *path, U8 *format, ...)
{//Append to doc tree branch in file.
    CDoc *doc = DocRead(name);
    Bool  res = DocTreeAppendJoin(doc, path, TRUE, format, argc, argv);

    DocDel(doc);

    return res;
}

#help_index "DolDoc/Compiler;Compiler"
public I64 ExeDoc(CDoc *doc, I64 ccf_flags=0)
{//JIT Compile and execute a document.
    I64          res;
    Bool         okay = TRUE, unlock_doc=DocLock(doc);
    CCompCtrl   *cc = CompCtrlNew(, ccf_flags | CCF_DONT_FREE_BUF);

    if (Fs->last_cc != &Fs->next_cc)
        cc->opts = Fs->last_cc->opts;
    QueueInsert(cc, Fs->last_cc);
    LexAttachDoc(cc,, doc);
    try
    {
        Lex(cc);
        res = ExeCmdLine(cc);
    }
    catch
    {
        if (Fs->except_ch == 'Compiler' || Fs->except_ch == 'Break')
        {
            Fs->catch_except = TRUE;
            okay = FALSE;
            res = 0;
        }
    }
    QueueRemove(cc);
    if (okay)
        CompCtrlDel(cc); //TODO: can crash
    if (unlock_doc)
        DocUnlock(doc);

    return res;
}

#help_index "DolDoc/Tree;DolDoc/Compiler;Compiler"
public I64 DocTreeExe(CDoc *doc, U8 *path)
{//Execute doc tree branch.
    CDoc        *doc2;
    Bool         unlock_doc = DocLock(doc);
    CDocEntry   *tree_branch, *start_indent, *end_indent;
    I64          res = 0;

    if (DocTreeFind(doc, path, &tree_branch, &start_indent, &end_indent))
    {
        doc2=DocCopy(doc, tree_branch, end_indent);
        res=ExeDoc(doc2);
        DocDel(doc2);
    }
    if (unlock_doc)
        DocUnlock(doc);

    return res;
}

public I64 DocTreeFExe(U8 *name, U8 *path)
{//Execute doc tree branch in file.
    I64   res;
    CDoc *doc = DocRead(name);

    res = DocTreeExe(doc, path);
    DocDel(doc);

    return res;
}