Merge with 3.3

This commit is contained in:
Terry Jan Reedy 2013-08-19 01:05:41 -04:00
commit 86d26238d3
1 changed files with 37 additions and 25 deletions

View File

@ -1,19 +1,28 @@
'''Define SearchEngine for search dialogs.'''
import re import re
from tkinter import * from tkinter import *
import tkinter.messagebox as tkMessageBox import tkinter.messagebox as tkMessageBox
def get(root): def get(root):
'''Return the singleton SearchEngine instance for the process.
The single SearchEngine saves settings between dialog instances.
If there is not a SearchEngine already, make one.
'''
if not hasattr(root, "_searchengine"): if not hasattr(root, "_searchengine"):
root._searchengine = SearchEngine(root) root._searchengine = SearchEngine(root)
# XXX This will never garbage-collect -- who cares # This creates a cycle that persists until root is deleted.
return root._searchengine return root._searchengine
class SearchEngine: class SearchEngine:
"""Handles searching a text widget for Find, Replace, and Grep."""
def __init__(self, root): def __init__(self, root):
'''Initialize Variables that save search state.
The dialogs bind these to the UI elements present in the dialogs.
'''
self.root = root self.root = root
# State shared by search, replace, and grep;
# the search dialogs bind these to UI elements.
self.patvar = StringVar(root) # search pattern self.patvar = StringVar(root) # search pattern
self.revar = BooleanVar(root) # regular expression? self.revar = BooleanVar(root) # regular expression?
self.casevar = BooleanVar(root) # match case? self.casevar = BooleanVar(root) # match case?
@ -56,6 +65,7 @@ class SearchEngine:
return pat return pat
def getprog(self): def getprog(self):
"Return compiled cooked search pattern."
pat = self.getpat() pat = self.getpat()
if not pat: if not pat:
self.report_error(pat, "Empty regular expression") self.report_error(pat, "Empty regular expression")
@ -77,7 +87,7 @@ class SearchEngine:
return prog return prog
def report_error(self, pat, msg, col=-1): def report_error(self, pat, msg, col=-1):
# Derived class could overrid this with something fancier # Derived class could override this with something fancier
msg = "Error: " + str(msg) msg = "Error: " + str(msg)
if pat: if pat:
msg = msg + "\np\Pattern: " + str(pat) msg = msg + "\np\Pattern: " + str(pat)
@ -92,25 +102,23 @@ class SearchEngine:
self.setpat(pat) self.setpat(pat)
def search_text(self, text, prog=None, ok=0): def search_text(self, text, prog=None, ok=0):
"""Search a text widget for the pattern. '''Return (lineno, matchobj) for prog in text widget, or None.
If prog is given, it should be the precompiled pattern. If prog is given, it should be a precompiled pattern.
Return a tuple (lineno, matchobj); None if not found. Wrap (yes/no) and direction (forward/back) settings are used.
This obeys the wrap and direction (back) settings. The search starts at the selection (if there is one) or at the
insert mark (otherwise). If the search is forward, it starts
The search starts at the selection (if there is one) or at the right of the selection; for a backward search, it
at the insert mark (otherwise). If the search is forward, starts at the left end. An empty match exactly at either end
it starts at the right of the selection; for a backward of the selection (or at the insert mark if there is no
search, it starts at the left end. An empty match exactly selection) is ignored unless the ok flag is true -- this is
at either end of the selection (or at the insert mark if done to guarantee progress.
there is no selection) is ignored unless the ok flag is true
-- this is done to guarantee progress.
If the search is allowed to wrap around, it will return the If the search is allowed to wrap around, it will return the
original selection if (and only if) it is the only match. original selection if (and only if) it is the only match.
'''
"""
if not prog: if not prog:
prog = self.getprog() prog = self.getprog()
if not prog: if not prog:
@ -179,10 +187,11 @@ class SearchEngine:
col = len(chars) - 1 col = len(chars) - 1
return None return None
# Helper to search backwards in a string.
# (Optimized for the case where the pattern isn't found.)
def search_reverse(prog, chars, col): def search_reverse(prog, chars, col):
'''Search backwards in a string (line of text).
This is done by searching forwards until there is no match.
'''
m = prog.search(chars) m = prog.search(chars)
if not m: if not m:
return None return None
@ -198,10 +207,9 @@ def search_reverse(prog, chars, col):
i, j = m.span() i, j = m.span()
return found return found
# Helper to get selection end points, defaulting to insert mark.
# Return a tuple of indices ("line.col" strings).
def get_selection(text): def get_selection(text):
'''Return tuple of 'line.col' indexes from selection or insert mark.
'''
try: try:
first = text.index("sel.first") first = text.index("sel.first")
last = text.index("sel.last") last = text.index("sel.last")
@ -213,8 +221,12 @@ def get_selection(text):
last = first last = first
return first, last return first, last
# Helper to parse a text index into a (line, col) tuple.
def get_line_col(index): def get_line_col(index):
'''Return (line, col) tuple of ints from 'line.col' string.'''
line, col = map(int, index.split(".")) # Fails on invalid index line, col = map(int, index.split(".")) # Fails on invalid index
return line, col return line, col
##if __name__ == "__main__":
## from test import support; support.use_resources = ['gui']
## import unittest
## unittest.main('idlelib.idle_test.test_searchengine', verbosity=2, exit=False)