#@+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
