#@+leo-ver=4-thin
#@+node:EKR.20040502195118:@file-thin ../scripts/leoFindScript.py
# This file contains functions for non-interactive searching.
# You might find these useful while running other scripts.

import leo, string, re

#@+others
#@+node:EKR.20040502195118.1:changeAll
def changeAll ( commander, findPat, changePat, bodyFlag = 1 ):
    """
    changeAll       make changes in an entire Leo outline.

    commander       Commands object for a Leo outline window.
    findPat         the search string.
    changePat       the replacement string.
    bodyFlag        true: change body text.  false: change headline text.
    """
    n = len(changePat)
    v = commander.rootVnode()
    pos = 0
    while v != None:
        v, pos = changeNext(v, pos, findPat, changePat, bodyFlag)
        pos = pos + n
#@nonl
#@-node:EKR.20040502195118.1:changeAll
#@+node:EKR.20040502195118.2:changeNext
def changeNext ( v, pos, findPat, changePat, bodyFlag = 1 ):
    """
    changeNext:     use string.find() to change text in a Leo outline.

    v                       the vnode to start the search.
    pos                     the position within the body text of v to start the search.
    findPat         the search string.
    changePat       the replacement string.
    bodyFlag        true: change body text.  false: change headline text.

    returns a tuple (v,pos) showing where the change occured.
    returns (None,0) if no further match in the outline was found.

    Note: if (v,pos) is a tuple returned previously from changeNext,
    changeNext(v,pos+len(findPat),findPat,changePat)
    changes the next matching string.
    """
    n = len(findPat)
    v, pos = findNext(v, pos, findPat, bodyFlag)
    if v == None:
        return None, 0
    if bodyFlag:
        s = v.bodyString()
        # s[pos:pos+n] = changePat
        s = s[:pos] + changePat + s[pos+n:]
        v.setBodyStringOrPane(s)
    else:
        s = v.headString()
        # s[pos:pos+n] = changePat
        s = s[:pos] + changePat + s[pos+n:]
        v.setHeadStringOrHeadline(s)
        print("setting head string: ", result)
    return v, pos
#@nonl
#@-node:EKR.20040502195118.2:changeNext
#@+node:EKR.20040502195118.3:changePrev
def changePrev ( v, pos, findPat, changePat, bodyFlag = 1 ):
    """
    changePrev:     use string.rfind() to change text in a Leo outline.

    v                       the vnode to start the search.
    pos                     the position within the body text of v to start the search.
    findPat         the search string.
    changePat       the replacement string.
    bodyFlag        true: change body text.  false: change headline text.

    returns a tuple (v,pos) showing where the change occured.
    returns (None,0) if no further match in the outline was found.

    Note: if (v,pos) is a tuple returned previously from changePrev,
    changePrev(v,pos-len(findPat),findPat,changePat)
    changes the next matching string.
    """
    n = len(findPat)
    v, pos = findPrev(v, pos, findPat, bodyFlag)
    if v == None:
        return None, 0
    if bodyFlag:
        s = v.bodyString()
        # s[pos:pos+n] = changePat
        s = s[:pos] + changePat + s[pos+n:]
        v.setBodyStringOrPane(s)
    else:
        s = v.headString()
        #s[pos:pos+n] = changePat
        s = s[:pos] + changePat + s[pos+n:]
        v.setHeadStringOrHeadline(s)
    return v, pos
#@nonl
#@-node:EKR.20040502195118.3:changePrev
#@+node:EKR.20040502195118.4:findAll
def findAll(c,pattern,bodyFlag=1):
    """
    findAll         search an entire Leo outline for a pattern.

    c        commander for a Leo outline window.
    pattern         the search string.
    bodyFlag        true: search body text. false: search headline text.

    returns a list of tuples (v,pos) showing where matches occured.
    returns [] if no match were found.
    """
    v = c.rootVnode()
    n = len(pattern)
    result = [] ; pos = 0
    while v != None:
        v, pos = findNext(v,pos,pattern,bodyFlag)
        if v:
            result.append ((v, pos),)
        pos = pos + n
    return result
#@nonl
#@-node:EKR.20040502195118.4:findAll
#@+node:EKR.20040502195118.5:findNext
def findNext ( v, pos, pattern, bodyFlag = 1 ):
    """
    findNext:       use string.find() to find a pattern in a Leo outline.

    v                       the vnode to start the search.
    pos                     the position within the body text of v to start the search.
    pattern         the search string.
    bodyFlag        true: search body text.  false: search headline text.

    returns a tuple (v,pos) showing where the match occured.
    returns (None,0) if no further match in the outline was found.

    Note: if (v,pos) is a tuple returned previously from findNext,
    findNext(v,pos+len(pattern),pattern) finds the next match.
    """
    while v != None:
        if bodyFlag:
            s = v.bodyString()
        else:
            s = v.headString()
        pos = s.find(pattern,pos )
        if pos != -1:
            return v, pos
        v = v.threadNext()
        pos = 0
    return None, 0
#@nonl
#@-node:EKR.20040502195118.5:findNext
#@+node:EKR.20040502195118.6:findPrev
def findPrev ( v, pos, pattern, bodyFlag = 1 ):
    """
    findPrev:       use string.rfind() to find a pattern in a Leo outline.

    v                       the vnode to start the search.
    pos                     the position within the body text of v to start the search.
    pattern         the search string
    bodyFlag        true: search body text.  false: search headline text.

    returns a tuple (v,pos) showing where the match occured.
    returns (None,0) if no further match in the outline was found.

    Note: if (v,pos) is a tuple returned previously from findPrev,
    findPrev(v,pos-len(pattern),pattern) finds the next match.
    """
    while v != None:
        if bodyFlag:
            s = v.bodyString()
        else:
            s = v.headString()
        pos = s.rfind(pattern,0,pos)
        if pos != -1:
            return v, pos
        v  = v.threadBack()
        pos = -1
    return None, 0
#@nonl
#@-node:EKR.20040502195118.6:findPrev
#@+node:EKR.20040502195118.7:reChangeAll
def reChangeAll ( commander, findPat, changePat, bodyFlag, reFlags = None ):
    """
    reChangeAll: make changes in an entire Leo outline using re module.

    commander       Commands object for a Leo outline window.
    findPat         the search string.
    changePat       the replacement string.
    bodyFlag        true: change body text.  false: change headline text.
    reFlags         flags argument to re.search().
    """
    n = len(changePat)
    v = commander.rootVnode()
    pos = 0
    while v != None:
        v, mo, pos = reChangeNext(
                v, pos, findPat, changePat, bodyFlag, reFlags)
        pos = pos + n
#@nonl
#@-node:EKR.20040502195118.7:reChangeAll
#@+node:EKR.20040502195118.8:reChangeNext
def reChangeNext ( v, pos, findPat, changePat, bodyFlag, reFlags = None ):
    """
    reChangeNext: use re.search() to change text in a Leo outline.

    v                       the vnode to start the search.
    pos                     the position within the body text of v to start the search.
    findPat         the search string.
    changePat       the replacement string.
    bodyFlag        true: change body text.  false: change headline text.
    reFlags         flags argument to re.search().

    returns a tuple (v,pos) showing where the change occured.
    returns (None,0) if no further match in the outline was found.

    Note: if (v,pos) is a tuple returned previously from reChangeNext,
    reChangeNext(v,pos+len(findPat),findPat,changePat,bodyFlag)
    changes the next matching string.
    """
    n = len(findPat)
    v, mo, pos = reFindNext(v, pos, findPat, bodyFlag, reFlags)
    if v == None:
        return None, None, 0
    if bodyFlag:
        s = v.bodyString()
        print(s, findPat, changePat)
        # s[pos:pos+n] = changePat
        s = s[:pos] + changePat + s[pos+n:]
        v.setBodyStringOrPane(s)
    else:
        s = v.headString()
        # s[pos:pos+n] = changePat
        s = s[:pos] + changePat + s[pos+n:]
        v.setHeadStringOrHeadline(s)
    return v, mo, pos
#@nonl
#@-node:EKR.20040502195118.8:reChangeNext
#@+node:EKR.20040502195118.9:reChangePrev
def reChangePrev ( v, pos, findPat, changePat, bodyFlag, reFlags = None ):
    """
    reChangePrev: use re.search() to change text in a Leo outline.

    v                       the vnode to start the search.
    pos                     the position within the body text of v to start the search.
    findPat         the search string.
    changePat       the replacement string.
    bodyFlag        true: change body text.  false: change headline text.
    reFlags         flags argument to re.search().

    returns a tuple (v,pos) showing where the change occured.
    returns (None,0) if no further match in the outline was found.

    Note: if (v,pos) is a tuple returned previously from reChangePrev,
    reChangePrev(v,pos-len(findPat),findPat,changePat,bodyFlag)
    changes the next matching string.
    """
    n = len(findPat)
    v, mo, pos = reFindPrev(v, pos, findPat, bodyFlag, reFlags)
    if v == None:
        return None, None, 0
    if bodyFlag:
        s = v.bodyString()
        # s[pos:pos+n] = changePat
        s = s[:pos] + changePat + s[pos+n:]
        v.setBodyStringOrPane(s)
    else:
        s = v.headString()
        # s[pos:pos+n] = changePat
        s = s[:pos] + changePat + s[pos+n:]
        v.setHeadStringOrHeadline(s)
    return v, mo, pos
#@nonl
#@-node:EKR.20040502195118.9:reChangePrev
#@+node:EKR.20040502195118.10:reFindAll
def reFindAll(c,findPat,bodyFlag,reFlags=None):
    """
    reFindAll       search an entire Leo outline using re module.

    c            commander for a Leo outline window.
    pattern         the search string.
    bodyFlag        true: search body text.  false: search headline text.
    reFlags         flags argument to re.search().

    returns a list of tuples (v,pos) showing where matches occured.
    returns [] if no match were found.
    """
    v = c.rootVnode()
    n = len(findPat)
    result = [] ; pos = 0
    while v != None:
        v, mo, pos = reFindNext(v,pos,findPat,bodyFlag,reFlags)
        if v != None:
            result.append ( (v,mo,pos) )
        pos = pos + n
    return result
#@nonl
#@-node:EKR.20040502195118.10:reFindAll
#@+node:EKR.20040502195118.11:reFindNext
def reFindNext ( v, pos, pattern, bodyFlag, reFlags = None ):
    """
    reFindNext:     use re.search() to find pattern in a Leo outline.

    v                       the vnode to start the search.
    pos                     the position within the body text of v to start the search.
    pattern         the search string
    bodyFlag        true: search body text.  false: search headline text.
    reFlags         the flags argument to re.search()

    returns a tuple (v,mo,pos) showing where the match occured.
    returns (None,None,0) if no further match in the outline was found.
    mo is a "match object"

    Note: if (v,pos) is a tuple returned previously from reFindNext,
    reFindNext(v,pos+len(pattern),pattern) finds the next match.
    """
    while v != None:
        if bodyFlag:
            s = v.bodyString()
        else:
            s = v.headString()
        if reFlags == None:
            mo = re.search ( pattern, s[pos:] )
        else:
            mo = re.search ( pattern, s[pos:], reFlags )
        if mo != None:
            return v, mo, pos + mo.start()
        v = v.threadNext()
        pos = 0
    return None, None, 0
#@nonl
#@-node:EKR.20040502195118.11:reFindNext
#@+node:EKR.20040502195118.12:reFindPrev
def reFindPrev ( v, pos, pattern, bodyFlag, reFlags = None ):
    """
    reFindPrev:     use re.search() to find pattern in a Leo outline.

    v                       the vnode to start the search.
    pos                     the position within the body text of v to start the search.
    pattern         the search string
    bodyFlag        true: search body text.  false: search headline text.
    reFlags         the flags argument to re.search()

    returns a tuple (v,mo,pos) showing where the match occured.
    returns (None,None,0) if no further match in the outline was found.

    Note 1: Searches vnodes in reverse (v.threadBack) direction.
    Searches text of vnodes in _forward_ direction.

    Note 2: if (v,pos) is a tuple returned previously from reFindPrev,
    reFindPrev(v,pos-len(pattern),pattern) finds the next match.
    """
    while v != None:
        if bodyFlag:
            s = v.bodyString()
        else:
            s = v.headString()
        # Forward search through text...
        if reFlags == None:
            mo = re.search ( pattern, s[pos:] )
        else:
            mo = re.search ( pattern, s[pos:], reFlags )
        if mo != None:
            return v, mo, pos+mo.start()
        # Reverse search through vnode.
        v = v.threadBack()
        pos = 0
    return None, None, 0
#@nonl
#@-node:EKR.20040502195118.12:reFindPrev
#@+node:EKR.20040502195118.13:lineAtPos
def lineAtPos ( s, pos ):
    """
    lineAtPos: return the line of a string containing the given index.
    s               a string
    pos             an index into s
    """
    # find the start of the line containing the match
    if len(s) < 1:
        return ""
    if pos > len(s):
        pos = len(s)-1

    while pos > 0:
        if s[pos] == '\n':
            pos = pos + 1
            break
        else:
            pos = pos - 1
    # return the line containing the match
    s = s[pos:]
    list = s.split("\n")
    return list[0]
#@nonl
#@-node:EKR.20040502195118.13:lineAtPos
#@+node:EKR.20040502195118.14:printFindList
def printFindList( findList, bodyFlag = 1 ):
    """
    printFindList:  Print matching lines from the list.

    findList:               a list of (v,pos) tuples returned from findAll().
    Only the line containing the match is printed.
    Lines are printed once for each match found on the line.
    """
    for v,pos in findList:
        if v != None:
            if bodyFlag:
                s = v.bodyString()
            else:
                s = v.headString()
            print(lineAtPos(s, pos))
#@nonl
#@-node:EKR.20040502195118.14:printFindList
#@-others
#@nonl
#@-node:EKR.20040502195118:@file-thin ../scripts/leoFindScript.py
#@-leo
