#help_index "DolDoc/Editor"

public Bool DocGoToLine(CDoc *doc, I64 line_num) //one based
{//Nearest to specified line num.  Move cur_entry & center.
        Bool res = FALSE, unlock;

        if (doc)
        {
                unlock = DocLock(doc);
                doc->x = 0;
                doc->y = line_num - 1;
                DocRecalc(doc, RECALCt_FIND_CURSOR);
                DocCenter(doc);
                if (doc->cur_entry->y == line_num - 1)
                        res = TRUE;
                if (unlock)
                        DocUnlock(doc);
        }

        return res;
}

public Bool DocFind(CDoc *haystack_doc, I64 start_line_num=I64_MIN, U8 *needle, I64 match=1)
{//Find str by searching tags. Move cur_entry & center.
        Bool             res = FALSE, unlock;
        CDocEntry       *doc_e;
        U8                      *ptr;
        I64                      i;

        if (haystack_doc)
        {
                unlock = DocLock(haystack_doc);
                if (start_line_num == I64_MIN)
                {
                        res = TRUE;
                        doc_e = haystack_doc->head.next;
                }
                else
                {
                        res = DocGoToLine(haystack_doc, start_line_num);
                        doc_e = haystack_doc->cur_entry;
                }
                if (res)
                {
                        if (needle)
                        {
                                res = FALSE;
                                while (doc_e != haystack_doc)
                                {
                                        if (doc_e->de_flags & DOCEF_TAG && doc_e->tag && //TODO: handle multi-DocEntry strs
                                                (ptr = StrIMatch(needle, doc_e->tag)))
                                        {
                                                i = ptr-doc_e->tag;
                                                if (!--match)
                                                {
                                                        haystack_doc->cur_entry = doc_e;
                                                        if (i < doc_e->min_col)
                                                                i = doc_e->min_col;
                                                        if (i > doc_e->max_col)
                                                                i = doc_e->max_col;
                                                        haystack_doc->cur_col = i;
                                                        res = TRUE;
                                                        break;
                                                }
                                        }
                                        doc_e = doc_e->next;
                                }
                        }
                        else
                                res = FALSE;
                }
                if (!res)
                        DocBottom(haystack_doc);
                DocCenter(haystack_doc);
                if (unlock)
                        DocUnlock(haystack_doc);
        }

        return res;
}

public Bool DocAnchorFind(CDoc *haystack_doc, U8 *needle_str)
{//Find named anchor. Move cur_entry & center.
        Bool             res = FALSE, unlock;
        CDocEntry       *doc_e;

        if (haystack_doc)
        {
                unlock = DocLock(haystack_doc);
                doc_e = haystack_doc->head.next;
                if (needle_str)
                        while (doc_e != haystack_doc)
                        {
                                if (doc_e->type_u8 == DOCT_ANCHOR && doc_e->de_flags & DOCEF_AUX_STR)
                                {
                                        if (!StrCompare(needle_str, doc_e->aux_str))
                                        {
                                                haystack_doc->cur_entry = doc_e;
                                                haystack_doc->cur_col   = doc_e->min_col;
                                                res = TRUE;
                                                break;
                                        }
                                }
                                doc_e = doc_e->next;
                        }
                if (!res)
                        DocBottom(haystack_doc);
                DocCenter(haystack_doc);
                if (unlock)
                        DocUnlock(haystack_doc);
        }

        return res;
}

public U0 EdFindNext(CDoc *doc)
{//Editor F3 find next,  possibly doing replaces.
        Bool             unlock = DocLock(doc);
        U8                      *ptr, *ptr2, *ptr3;
        CDocEntry       *doc_ce = doc->cur_entry, *doc_e = doc_ce;
        I64                      sf_flags;

        if (doc->find_replace->match_case)
                sf_flags = 0;
        else
                sf_flags = SFF_IGNORE_CASE;
        if (doc->find_replace->whole_labels)
                sf_flags |= SFG_WHOLE_LABELS;
        do
        {
                if (doc_e != doc)
                {
                        if (doc_e->de_flags & DOCEF_TAG && doc_e->tag && !(doc_e->de_flags & (DOCEG_DONT_EDIT | DOCEF_FILTER_SKIP)))
                        {
                                if (doc_e->type & DOCET_SEL || !doc->find_replace->scan_sel_text)
                                {
                                        if (doc->find_replace->scan_fwd)
                                        {
                                                if (doc_e == doc_ce)
                                                {
                                                        ptr = doc_ce->tag + doc->cur_col + 1;
                                                        if (ptr - doc_ce->tag >= doc_ce->max_col)
                                                                goto fn_skip;
                                                        if (ptr - doc_ce->tag < doc_ce->min_col)
                                                                ptr = doc_ce->tag + doc_ce->min_col;
                                                }
                                                else
                                                        ptr = doc_e->tag;
                                                if (ptr = StrFind(doc->find_replace->find_text, ptr, sf_flags))
                                                {
                                                        doc->cur_entry  = doc_e;
                                                        doc->cur_col    = ptr-doc_e->tag;
                                                        if (doc->cur_col >= doc_e->max_col)
                                                                doc->cur_col = doc_e->max_col - 1;
                                                        if (doc->cur_col < doc_e->min_col)
                                                                doc->cur_col = doc_e->min_col;
                                                        DocCenter(doc);
                                                        if (unlock)
                                                                DocUnlock(doc);
                                                        return;
                                                }
                                        }
                                        else
                                        {
                                                ptr2 = NULL;
                                                ptr = doc_e->tag + doc_e->min_col;
                                                if (doc_e == doc_ce)
                                                        ptr3 = doc_ce->tag + doc->cur_col;
                                                else
                                                        ptr3 = doc_e->tag + doc_e->max_col;
                                                while (ptr = StrFind(doc->find_replace->find_text, ptr, sf_flags))
                                                {
                                                        if (ptr >= ptr3)
                                                                break;
                                                        ptr2 = ptr++;
                                                }
                                                if (ptr2 && ptr2 < ptr3)
                                                {
                                                        doc->cur_entry  = doc_e;
                                                        doc->cur_col    = ptr2 - doc_e->tag;
                                                        if (doc->cur_col >= doc_e->max_col)
                                                                doc->cur_col = doc_e->max_col - 1;
                                                        if (doc->cur_col < doc_e->min_col)
                                                                doc->cur_col = doc_e->min_col;
                                                        DocCenter(doc);
                                                        if (unlock)
                                                                DocUnlock(doc);

                                                        return;
                                                }
                                        }
                                }
                        }
                }
fn_skip:
                if (doc->find_replace->scan_fwd)
                        doc_e = doc_e->next;
                else
                        doc_e = doc_e->last;
        }
        while (doc_e != doc_ce);

        if (unlock)
                DocUnlock(doc);
}

public U0 EdSelAll(CDoc *doc, Bool sel)
{//Set state of DOCET_SEL on all entries.
        Bool             unlock = DocLock(doc);
        CDocEntry       *doc_e = doc->head.next;

        while (doc_e != doc)
        {
                BEqual(&doc_e->type, DOCEt_SEL, sel);
                doc_e = doc_e->next;
        }
        if (unlock)
                DocUnlock(doc);
}

public Bool EdFindPaired(CDoc *doc, U8 plus, U8 minus, 
Bool forward, Bool abort_on_dbl_colon=FALSE)
{//Find { } or ( ) pair. Move cur_entry & center.
        Bool             unlock = DocLock(doc), res = FALSE;
        U8                      *ptr;
        I64                      ch, levels = 0, colons = 0, original_col = doc->cur_col;
        CDocEntry       *doc_ce = doc->cur_entry, *doc_e = doc_ce, *original_ce = doc_ce;

        if (abort_on_dbl_colon && EdCurU8(doc) == ':')
                colons = 1;
        else
                colons = 0;
        do
        {
                if (doc_e != doc)
                {
                        if (doc_e->de_flags & DOCEF_TAG && doc_e->tag && !(doc_e->de_flags & DOCEF_FILTER_SKIP))
                        {
                                if (forward)
                                {
                                        if (doc_e == doc_ce)
                                                ptr = doc_e->tag + doc->cur_col + 1;
                                        else
                                                ptr = doc_e->tag;
                                        if (ptr - doc_e->tag < doc_e->min_col)
                                                ptr = doc_e->tag + doc_e->min_col;
                                        if (ptr - doc_e->tag >= doc_e->max_col)
                                                goto pa_skip;
                                        while (ch = *ptr++)
                                                if (abort_on_dbl_colon && ch == ':')
                                                {
                                                        if (++colons == 2)
                                                        {
                                                                doc->cur_entry  = doc_e;
                                                                doc->cur_col    = ptr - doc_e->tag - 1;
                                                                EdCursorLeft(doc);
                                                                res = FALSE;
                                                                goto pa_done;
                                                        }
                                                }
                                                else
                                                {
                                                        colons = 0;
                                                        if (ch == plus)
                                                                levels++;
                                                        else if (ch == minus)
                                                        {
                                                                if (!levels--)
                                                                {
                                                                        doc->cur_entry  = doc_e;
                                                                        doc->cur_col    = ptr - doc_e->tag - 1;
                                                                        res = doc->cur_entry != original_ce || doc->cur_col != original_col;
                                                                        goto pa_done;
                                                                }
                                                        }
                                                }
                                }
                                else
                                {
                                        if (doc_e == doc_ce)
                                        {
                                                ptr = doc_e->tag + doc->cur_col - 1;
                                                if (ptr - doc_e->tag >= doc_e->max_col)
                                                        ptr = doc_e->tag + doc_e->max_col - 1;
                                        }
                                        else
                                                ptr = doc_e->tag + doc_e->max_col - 1;
                                        if (ptr - doc_e->tag < doc_e->min_col)
                                                goto pa_skip;
                                        while (ptr >= doc_e->tag + doc_e->min_col)
                                        {
                                                ch = *ptr--;
                                                if (abort_on_dbl_colon && ch == ':')
                                                {
                                                        if (++colons == 2)
                                                        {
                                                                doc->cur_entry  = doc_e;
                                                                doc->cur_col    = ptr - doc_e->tag + 1;
                                                                res = FALSE;
                                                                goto pa_done;
                                                        }
                                                }
                                                else
                                                {
                                                        colons = 0;
                                                        if (ch == plus)
                                                                levels++;
                                                        else if (ch == minus)
                                                        {
                                                                if (!levels--)
                                                                {
                                                                        doc->cur_entry  = doc_e;
                                                                        doc->cur_col    = ptr-doc_e->tag+1;
                                                                        res = doc->cur_entry != original_ce || doc->cur_col != original_col;
                                                                        goto pa_done;
                                                                }
                                                        }
                                                }
                                        }
                                }
                        }
                }
pa_skip:
                if (forward)
                        doc_e = doc_e->next;
                else
                        doc_e = doc_e->last;
        }
        while (doc_e != doc_ce);

pa_done:
        DocRecalc(doc);
        DocCenter(doc);
        if (unlock)
                DocUnlock(doc);

        return res;
}

public Bool EdGoToFun(CDoc *doc, Bool forward, Bool abort_on_dbl_colon)
{//Move cur_entry to start of cur fun and center.(Shoddy)
        Bool unlock = DocLock(doc), res = FALSE;
        I64  ch, levels, colons;

        if (forward)
        {
                levels = 0;
                colons = 0;
                while (doc->cur_entry != doc)
                {
                        ch = EdCurU8(doc);
                        if (abort_on_dbl_colon && ch == ':')
                        {
                                if (++colons == 2)
                                {
                                        EdCursorLeft(doc);
                                        break;
                                }
                        }
                        else
                        {
                                colons = 0;
                                if (ch == '{')
                                        levels++;
                                else if (ch == '}' && !levels--)
                                        break;
                        }
                        EdCursorRight(doc);
                }
                DocRecalc(doc);
                if (doc->cur_entry != doc)
                        res = TRUE;
        }
        else
        {
                while (EdFindPaired(doc, '}', '{', FALSE, abort_on_dbl_colon));
                if (doc->cur_entry != doc)
                {
                        ch = EdCurU8(doc);
                        if (abort_on_dbl_colon && ch == ':')
                                res = TRUE;
                        else
                        {
                                if (ch == '{')
                                        res = TRUE;
                        }
                }
        }
        if (unlock)
                DocUnlock(doc);

        return res;
}

public U0 EdSelFun(CDoc *doc, Bool abort_on_dbl_colon=FALSE)
{//Set DOCET_SEL on all entries in cur fun.
        Bool             unlock = DocLock(doc);
        U8                      *ptr;
        I64                      ch, levels = 0, colons = 0;
        CDocEntry       *doc_e;

        EdSelAll(doc, FALSE);
        EdGoToFun(doc, FALSE, abort_on_dbl_colon);
        if (EdCurU8(doc) == '{')
                levels--;
        else if (abort_on_dbl_colon && EdCurU8(doc) == ':')
        {
                EdCursorRight(doc);
                if (EdCurU8(doc) == ':')
                        EdCursorRight(doc);
        }
        doc_e = doc->cur_entry;
        while (doc_e != doc)
        {
                doc_e->type |= DOCET_SEL;
                if (doc_e->de_flags & DOCEF_TAG && doc_e->tag)
                {
                        ptr = doc_e->tag;
                        if (doc_e == doc->cur_entry)
                                ptr += doc->cur_col;
                        while (ch = *ptr++)
                                if (abort_on_dbl_colon && ch == ':')
                                {
                                        if (++colons == 2)
                                                goto sf_done;
                                }
                                else
                                {
                                        colons = 0;
                                        if (ch == '{')
                                                levels++;
                                        else if (ch == '}' && !levels--)
                                                goto sf_done;
                                }
                }
                doc_e = doc_e->next;
        }
sf_done:
        DocRecalc(doc);
        if (unlock)
                DocUnlock(doc);
}

#define RSAC_REPLACE    0
#define RSAC_SKIP               1
#define RSAC_ALL                2

I64 PopUpReplaceSkipAllCancel(U8 *header=NULL, U8 *footer=NULL)
{
        I64   i;
        CDoc *doc = DocNew;

        if (header)
                DocPrint(doc, "%s", header);
        DocPrint(doc,
                                "$CM+LX,1,4$$BT,\"REPLACE\",LE=RSAC_REPLACE$"
                                "$CM+LX,17,0$$BT,\"SKIP\",LE=RSAC_SKIP$"
                                "$CM+LX,1,3$$BT,\"ALL\",LE=RSAC_ALL$"
                                "$CM+LX,17,0$$BT,\"CANCEL\",LE=DOCM_CANCEL$\n");
        if (footer)
                DocPrint(doc, "%s", footer);
        i = PopUpMenu(doc);
        DocDel(doc);

        return i;
}

I64 EdFindReplace(CDoc *doc)
{
        Bool             found, unlock;
        I64                      cmd, i, j, plen, rlen, dlen, res = -1, sf_flags;
        U8                      *src, *dst, *dst2;
        CDocEntry       *doc_ce, *doc_e, *doc_marker = NULL;

        if (doc->find_replace->prompt)
                cmd = RSAC_REPLACE;
        else
                cmd = RSAC_ALL;
        if (!doc->find_replace->prompt || DocForm(doc->find_replace))
        {
                res = 0;
                unlock = DocLock(doc);
                if (doc->find_replace->match_case || doc->find_replace->local_var)
                        sf_flags = 0;
                else
                        sf_flags = SFF_IGNORE_CASE;
                if (doc->find_replace->whole_labels || doc->find_replace->local_var)
                        sf_flags |= SFG_WHOLE_LABELS;

                if (i = doc->find_replace->filter_lines)
                {
                        doc_ce = doc->head.next;
                        while (doc_ce != doc)
                        {
                                if (doc_ce->de_flags & DOCEF_TAG && doc_ce->tag && !(doc_ce->de_flags & DOCEF_FILTER_SKIP) &&
                                        StrFind(doc->find_replace->find_text, doc_ce->tag, sf_flags))
                                {
                                        doc_ce->type |= DOCET_SEL;
                                        res++;
                                }
                                else
                                        doc_ce->type &= ~DOCET_SEL;
                                doc_ce = doc_ce->next;
                        }

                        doc_ce = doc->head.next;
                        while (doc_ce != doc)
                        {
                                if (!(doc_ce->de_flags & DOCEF_FILTER_SKIP))
                                {
                                        found = FALSE;

                                        doc_e = doc_ce;
                                        while (doc_e != doc && doc_e->y > doc_ce->y - i)
                                        {
                                                if (doc_e->type & DOCET_SEL)
                                                {
                                                        found = TRUE;
                                                        break;
                                                }
                                                else
                                                        doc_e = doc_e->last;
                                        }

                                        if (!found)
                                        {
                                                doc_e = doc_ce;
                                                while (doc_e != doc && doc_e->y < doc_ce->y + i)
                                                {
                                                        if (doc_e->type & DOCET_SEL)
                                                        {
                                                                found = TRUE;
                                                                break;
                                                        }
                                                        else
                                                                doc_e = doc_e->next;
                                                }
                                        }

                                        if (!found)
                                                doc_ce->de_flags |= DOCEF_FILTER_SKIP;
                                }

                                doc_ce = doc_ce->next;
                        }
                        EdSelAll(doc, FALSE);
                        goto fr_unlock_done;
                }

                if (doc->find_replace->local_var)
                        EdSelFun(doc);

                if (!doc->find_replace->replace && !doc->find_replace->local_var)
                {
                        EdFindNext(doc);
                        goto fr_unlock_done;
                }
                plen = StrLen(doc->find_replace->find_text);
                if (!plen)
                        goto fr_unlock_done;
                rlen = StrLen(doc->find_replace->replace_text);
                if (doc->head.next != doc)
                {
                        doc_e = doc_marker = DocSplitTag(doc, doc->cur_entry, doc->cur_col,
                                                                        doc->cur_entry->x + doc->cur_col, doc->cur_entry->y, DOCT_MARKER);
                        do
                        {
                                if (doc_e == doc)
                                {
                                        if (doc->find_replace->scan_fwd)
                                                doc_e = doc_e->next;
                                        else
                                                doc_e = doc_e->last;
                                        if (doc_e == doc_marker)
                                                break;
                                }
                                if (doc_e->type_u8 == DOCT_TEXT &&
                                        !(doc_e->de_flags & (DOCEG_DONT_EDIT | DOCEF_FILTER_SKIP)) &&
                                        (doc_e->type & DOCET_SEL || !doc->find_replace->scan_sel_text && !doc->find_replace->local_var))
                                {
                                        src = doc_e->tag;
                                        while (src)
                                        {
                                                src = StrFind(doc->find_replace->find_text, src, sf_flags);
                                                if (src)
                                                {
                                                        doc->cur_col    = src - doc_e->tag;
                                                        doc->cur_entry  = doc_e;
                                                        if (cmd != RSAC_ALL)
                                                                DocCenter(doc);
                                                        if (cmd != RSAC_ALL)
                                                        {
                                                                DocUnlock(doc);
                                                                cmd = PopUpReplaceSkipAllCancel("");
                                                                DocLock(doc);
                                                                if (cmd < 0)
                                                                        goto fr_unlock_done;
                                                        }
                                                        doc_e = doc->cur_entry;
                                                        src = doc->cur_col + doc_e->tag;
                                                        if (cmd == RSAC_REPLACE || cmd == RSAC_ALL)
                                                        {
                                                                dlen = StrLen(doc_e->tag);
                                                                doc_e->max_col = dlen + rlen - plen;
                                                                dst = MAlloc(doc_e->max_col + 1, doc->mem_task);
                                                                dst2 = dst;
                                                                j = src - doc_e->tag;
                                                                for (i = 0; i < j; i++)
                                                                        *dst++ = doc_e->tag[i];
                                                                for (i = 0; i < rlen; i++)
                                                                        *dst++ = doc->find_replace->replace_text[i];
                                                                src = dst;
                                                                for (i = j + plen; i <= dlen; i++)
                                                                        *dst++ = doc_e->tag[i];
                                                                Free(doc_e->tag);
                                                                doc_e->tag = dst2;
                                                                doc->cur_col    = src - doc_e->tag;
                                                                doc->cur_entry  = doc_e;
                                                                if (cmd != RSAC_ALL)
                                                                {
                                                                        DocRemSoftNewLines(doc, doc->cur_entry);
                                                                        DocRecalc(doc);
                                                                }
                                                                doc_e = doc->cur_entry;
                                                                src = doc->cur_col + doc_e->tag;
                                                                res++;
                                                        }
                                                        else
                                                                src++;
                                                }
                                        }
                                }
                                if (doc->find_replace->scan_fwd)
                                        doc_e = doc_e->next;
                                else
                                        doc_e = doc_e->last;
                        }
                        while (doc_e != doc_marker);
                }
fr_unlock_done:
                if (doc_marker)
                        DocEntryDel(doc, doc_marker);
                DocRemSoftNewLines(doc, NULL);
                DocRecalc(doc);
                DocCenter(doc);
                if (unlock)
                        DocUnlock(doc);
        }

        return res;
}

public I64 EdReplace(CDoc *doc, U8 *find, U8 *replace, Bool sel=TRUE, Bool match_case=TRUE, Bool whole_labels=FALSE)
{//Find & replace using editor's cmd.
        CEdFindText     old_find_replace;
        Bool            unlock;
        I64                     i, res = -1;

        if (!doc)
                return -1;
        unlock = DocLock(doc);
        MemCopy(&old_find_replace, doc->find_replace, sizeof(CEdFindText));
        MemSet(doc->find_replace, 0, sizeof(CEdFindText));
        i = StrLen(find);
        if (i < sizeof(CEdFindText.find_text))
        {
                MemCopy(doc->find_replace->find_text, find, i + 1);
                i = StrLen(replace);
                if (i < sizeof(CEdFindText.replace_text))
                {
                        MemCopy(doc->find_replace->replace_text, replace, i + 1);
                        doc->find_replace->replace               = TRUE;
                        doc->find_replace->scan_sel_text = sel;
                        doc->find_replace->match_case    = match_case;
                        doc->find_replace->whole_labels  = whole_labels;
                        doc->find_replace->prompt                = FALSE;
                        res = EdFindReplace(doc);
                }
        }
        MemCopy(doc->find_replace, &old_find_replace, sizeof(CEdFindText));
        if (unlock)
                DocUnlock(doc);

        return res;
}

class CEdLineGoTo
{
        I64 line format "$DA,A=\"Go to Line:%d\"$";
};

U0 EdGoToLine(CDoc *doc)
{//Prompt with form and go to line num.
        CEdLineGoTo gtl;

        gtl.line = 1;
        if (DocForm(&gtl))
                DocGoToLine(doc, gtl.line);
}