PyXR

c:\python24\lib \ idlelib \ SearchEngine.py



0001 import re
0002 from Tkinter import *
0003 import tkMessageBox
0004 
0005 def get(root):
0006     if not hasattr(root, "_searchengine"):
0007         root._searchengine = SearchEngine(root)
0008         # XXX This will never garbage-collect -- who cares
0009     return root._searchengine
0010 
0011 class SearchEngine:
0012 
0013     def __init__(self, root):
0014         self.root = root
0015         # State shared by search, replace, and grep;
0016         # the search dialogs bind these to UI elements.
0017         self.patvar = StringVar(root)           # search pattern
0018         self.revar = BooleanVar(root)           # regular expression?
0019         self.casevar = BooleanVar(root)         # match case?
0020         self.wordvar = BooleanVar(root)         # match whole word?
0021         self.wrapvar = BooleanVar(root)         # wrap around buffer?
0022         self.wrapvar.set(1)                     # (on by default)
0023         self.backvar = BooleanVar(root)         # search backwards?
0024 
0025     # Access methods
0026 
0027     def getpat(self):
0028         return self.patvar.get()
0029 
0030     def setpat(self, pat):
0031         self.patvar.set(pat)
0032 
0033     def isre(self):
0034         return self.revar.get()
0035 
0036     def iscase(self):
0037         return self.casevar.get()
0038 
0039     def isword(self):
0040         return self.wordvar.get()
0041 
0042     def iswrap(self):
0043         return self.wrapvar.get()
0044 
0045     def isback(self):
0046         return self.backvar.get()
0047 
0048     # Higher level access methods
0049 
0050     def getcookedpat(self):
0051         pat = self.getpat()
0052         if not self.isre():
0053             pat = re.escape(pat)
0054         if self.isword():
0055             pat = r"\b%s\b" % pat
0056         return pat
0057 
0058     def getprog(self):
0059         pat = self.getpat()
0060         if not pat:
0061             self.report_error(pat, "Empty regular expression")
0062             return None
0063         pat = self.getcookedpat()
0064         flags = 0
0065         if not self.iscase():
0066             flags = flags | re.IGNORECASE
0067         try:
0068             prog = re.compile(pat, flags)
0069         except re.error, what:
0070             try:
0071                 msg, col = what
0072             except:
0073                 msg = str(what)
0074                 col = -1
0075             self.report_error(pat, msg, col)
0076             return None
0077         return prog
0078 
0079     def report_error(self, pat, msg, col=-1):
0080         # Derived class could overrid this with something fancier
0081         msg = "Error: " + str(msg)
0082         if pat:
0083             msg = msg + "\np\Pattern: " + str(pat)
0084         if col >= 0:
0085             msg = msg + "\nOffset: " + str(col)
0086         tkMessageBox.showerror("Regular expression error",
0087                                msg, master=self.root)
0088 
0089     def setcookedpat(self, pat):
0090         if self.isre():
0091             pat = re.escape(pat)
0092         self.setpat(pat)
0093 
0094     def search_text(self, text, prog=None, ok=0):
0095         """Search a text widget for the pattern.
0096 
0097         If prog is given, it should be the precompiled pattern.
0098         Return a tuple (lineno, matchobj); None if not found.
0099 
0100         This obeys the wrap and direction (back) settings.
0101 
0102         The search starts at the selection (if there is one) or
0103         at the insert mark (otherwise).  If the search is forward,
0104         it starts at the right of the selection; for a backward
0105         search, it starts at the left end.  An empty match exactly
0106         at either end of the selection (or at the insert mark if
0107         there is no selection) is ignored  unless the ok flag is true
0108         -- this is done to guarantee progress.
0109 
0110         If the search is allowed to wrap around, it will return the
0111         original selection if (and only if) it is the only match.
0112 
0113         """
0114         if not prog:
0115             prog = self.getprog()
0116             if not prog:
0117                 return None # Compilation failed -- stop
0118         wrap = self.wrapvar.get()
0119         first, last = get_selection(text)
0120         if self.isback():
0121             if ok:
0122                 start = last
0123             else:
0124                 start = first
0125             line, col = get_line_col(start)
0126             res = self.search_backward(text, prog, line, col, wrap, ok)
0127         else:
0128             if ok:
0129                 start = first
0130             else:
0131                 start = last
0132             line, col = get_line_col(start)
0133             res = self.search_forward(text, prog, line, col, wrap, ok)
0134         return res
0135 
0136     def search_forward(self, text, prog, line, col, wrap, ok=0):
0137         wrapped = 0
0138         startline = line
0139         chars = text.get("%d.0" % line, "%d.0" % (line+1))
0140         while chars:
0141             m = prog.search(chars[:-1], col)
0142             if m:
0143                 if ok or m.end() > col:
0144                     return line, m
0145             line = line + 1
0146             if wrapped and line > startline:
0147                 break
0148             col = 0
0149             ok = 1
0150             chars = text.get("%d.0" % line, "%d.0" % (line+1))
0151             if not chars and wrap:
0152                 wrapped = 1
0153                 wrap = 0
0154                 line = 1
0155                 chars = text.get("1.0", "2.0")
0156         return None
0157 
0158     def search_backward(self, text, prog, line, col, wrap, ok=0):
0159         wrapped = 0
0160         startline = line
0161         chars = text.get("%d.0" % line, "%d.0" % (line+1))
0162         while 1:
0163             m = search_reverse(prog, chars[:-1], col)
0164             if m:
0165                 if ok or m.start() < col:
0166                     return line, m
0167             line = line - 1
0168             if wrapped and line < startline:
0169                 break
0170             ok = 1
0171             if line <= 0:
0172                 if not wrap:
0173                     break
0174                 wrapped = 1
0175                 wrap = 0
0176                 pos = text.index("end-1c")
0177                 line, col = map(int, pos.split("."))
0178             chars = text.get("%d.0" % line, "%d.0" % (line+1))
0179             col = len(chars) - 1
0180         return None
0181 
0182 # Helper to search backwards in a string.
0183 # (Optimized for the case where the pattern isn't found.)
0184 
0185 def search_reverse(prog, chars, col):
0186     m = prog.search(chars)
0187     if not m:
0188         return None
0189     found = None
0190     i, j = m.span()
0191     while i < col and j <= col:
0192         found = m
0193         if i == j:
0194             j = j+1
0195         m = prog.search(chars, j)
0196         if not m:
0197             break
0198         i, j = m.span()
0199     return found
0200 
0201 # Helper to get selection end points, defaulting to insert mark.
0202 # Return a tuple of indices ("line.col" strings).
0203 
0204 def get_selection(text):
0205     try:
0206         first = text.index("sel.first")
0207         last = text.index("sel.last")
0208     except TclError:
0209         first = last = None
0210     if not first:
0211         first = text.index("insert")
0212     if not last:
0213         last = first
0214     return first, last
0215 
0216 # Helper to parse a text index into a (line, col) tuple.
0217 
0218 def get_line_col(index):
0219     line, col = map(int, index.split(".")) # Fails on invalid index
0220     return line, col
0221 

Generated by PyXR 0.9.4
SourceForge.net Logo