python-2.5.2/win32/Lib/idlelib/SearchEngine.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     1 import re
       
     2 from Tkinter import *
       
     3 import tkMessageBox
       
     4 
       
     5 def get(root):
       
     6     if not hasattr(root, "_searchengine"):
       
     7         root._searchengine = SearchEngine(root)
       
     8         # XXX This will never garbage-collect -- who cares
       
     9     return root._searchengine
       
    10 
       
    11 class SearchEngine:
       
    12 
       
    13     def __init__(self, root):
       
    14         self.root = root
       
    15         # State shared by search, replace, and grep;
       
    16         # the search dialogs bind these to UI elements.
       
    17         self.patvar = StringVar(root)           # search pattern
       
    18         self.revar = BooleanVar(root)           # regular expression?
       
    19         self.casevar = BooleanVar(root)         # match case?
       
    20         self.wordvar = BooleanVar(root)         # match whole word?
       
    21         self.wrapvar = BooleanVar(root)         # wrap around buffer?
       
    22         self.wrapvar.set(1)                     # (on by default)
       
    23         self.backvar = BooleanVar(root)         # search backwards?
       
    24 
       
    25     # Access methods
       
    26 
       
    27     def getpat(self):
       
    28         return self.patvar.get()
       
    29 
       
    30     def setpat(self, pat):
       
    31         self.patvar.set(pat)
       
    32 
       
    33     def isre(self):
       
    34         return self.revar.get()
       
    35 
       
    36     def iscase(self):
       
    37         return self.casevar.get()
       
    38 
       
    39     def isword(self):
       
    40         return self.wordvar.get()
       
    41 
       
    42     def iswrap(self):
       
    43         return self.wrapvar.get()
       
    44 
       
    45     def isback(self):
       
    46         return self.backvar.get()
       
    47 
       
    48     # Higher level access methods
       
    49 
       
    50     def getcookedpat(self):
       
    51         pat = self.getpat()
       
    52         if not self.isre():
       
    53             pat = re.escape(pat)
       
    54         if self.isword():
       
    55             pat = r"\b%s\b" % pat
       
    56         return pat
       
    57 
       
    58     def getprog(self):
       
    59         pat = self.getpat()
       
    60         if not pat:
       
    61             self.report_error(pat, "Empty regular expression")
       
    62             return None
       
    63         pat = self.getcookedpat()
       
    64         flags = 0
       
    65         if not self.iscase():
       
    66             flags = flags | re.IGNORECASE
       
    67         try:
       
    68             prog = re.compile(pat, flags)
       
    69         except re.error, what:
       
    70             try:
       
    71                 msg, col = what
       
    72             except:
       
    73                 msg = str(what)
       
    74                 col = -1
       
    75             self.report_error(pat, msg, col)
       
    76             return None
       
    77         return prog
       
    78 
       
    79     def report_error(self, pat, msg, col=-1):
       
    80         # Derived class could overrid this with something fancier
       
    81         msg = "Error: " + str(msg)
       
    82         if pat:
       
    83             msg = msg + "\np\Pattern: " + str(pat)
       
    84         if col >= 0:
       
    85             msg = msg + "\nOffset: " + str(col)
       
    86         tkMessageBox.showerror("Regular expression error",
       
    87                                msg, master=self.root)
       
    88 
       
    89     def setcookedpat(self, pat):
       
    90         if self.isre():
       
    91             pat = re.escape(pat)
       
    92         self.setpat(pat)
       
    93 
       
    94     def search_text(self, text, prog=None, ok=0):
       
    95         """Search a text widget for the pattern.
       
    96 
       
    97         If prog is given, it should be the precompiled pattern.
       
    98         Return a tuple (lineno, matchobj); None if not found.
       
    99 
       
   100         This obeys the wrap and direction (back) settings.
       
   101 
       
   102         The search starts at the selection (if there is one) or
       
   103         at the insert mark (otherwise).  If the search is forward,
       
   104         it starts at the right of the selection; for a backward
       
   105         search, it starts at the left end.  An empty match exactly
       
   106         at either end of the selection (or at the insert mark if
       
   107         there is no selection) is ignored  unless the ok flag is true
       
   108         -- this is done to guarantee progress.
       
   109 
       
   110         If the search is allowed to wrap around, it will return the
       
   111         original selection if (and only if) it is the only match.
       
   112 
       
   113         """
       
   114         if not prog:
       
   115             prog = self.getprog()
       
   116             if not prog:
       
   117                 return None # Compilation failed -- stop
       
   118         wrap = self.wrapvar.get()
       
   119         first, last = get_selection(text)
       
   120         if self.isback():
       
   121             if ok:
       
   122                 start = last
       
   123             else:
       
   124                 start = first
       
   125             line, col = get_line_col(start)
       
   126             res = self.search_backward(text, prog, line, col, wrap, ok)
       
   127         else:
       
   128             if ok:
       
   129                 start = first
       
   130             else:
       
   131                 start = last
       
   132             line, col = get_line_col(start)
       
   133             res = self.search_forward(text, prog, line, col, wrap, ok)
       
   134         return res
       
   135 
       
   136     def search_forward(self, text, prog, line, col, wrap, ok=0):
       
   137         wrapped = 0
       
   138         startline = line
       
   139         chars = text.get("%d.0" % line, "%d.0" % (line+1))
       
   140         while chars:
       
   141             m = prog.search(chars[:-1], col)
       
   142             if m:
       
   143                 if ok or m.end() > col:
       
   144                     return line, m
       
   145             line = line + 1
       
   146             if wrapped and line > startline:
       
   147                 break
       
   148             col = 0
       
   149             ok = 1
       
   150             chars = text.get("%d.0" % line, "%d.0" % (line+1))
       
   151             if not chars and wrap:
       
   152                 wrapped = 1
       
   153                 wrap = 0
       
   154                 line = 1
       
   155                 chars = text.get("1.0", "2.0")
       
   156         return None
       
   157 
       
   158     def search_backward(self, text, prog, line, col, wrap, ok=0):
       
   159         wrapped = 0
       
   160         startline = line
       
   161         chars = text.get("%d.0" % line, "%d.0" % (line+1))
       
   162         while 1:
       
   163             m = search_reverse(prog, chars[:-1], col)
       
   164             if m:
       
   165                 if ok or m.start() < col:
       
   166                     return line, m
       
   167             line = line - 1
       
   168             if wrapped and line < startline:
       
   169                 break
       
   170             ok = 1
       
   171             if line <= 0:
       
   172                 if not wrap:
       
   173                     break
       
   174                 wrapped = 1
       
   175                 wrap = 0
       
   176                 pos = text.index("end-1c")
       
   177                 line, col = map(int, pos.split("."))
       
   178             chars = text.get("%d.0" % line, "%d.0" % (line+1))
       
   179             col = len(chars) - 1
       
   180         return None
       
   181 
       
   182 # Helper to search backwards in a string.
       
   183 # (Optimized for the case where the pattern isn't found.)
       
   184 
       
   185 def search_reverse(prog, chars, col):
       
   186     m = prog.search(chars)
       
   187     if not m:
       
   188         return None
       
   189     found = None
       
   190     i, j = m.span()
       
   191     while i < col and j <= col:
       
   192         found = m
       
   193         if i == j:
       
   194             j = j+1
       
   195         m = prog.search(chars, j)
       
   196         if not m:
       
   197             break
       
   198         i, j = m.span()
       
   199     return found
       
   200 
       
   201 # Helper to get selection end points, defaulting to insert mark.
       
   202 # Return a tuple of indices ("line.col" strings).
       
   203 
       
   204 def get_selection(text):
       
   205     try:
       
   206         first = text.index("sel.first")
       
   207         last = text.index("sel.last")
       
   208     except TclError:
       
   209         first = last = None
       
   210     if not first:
       
   211         first = text.index("insert")
       
   212     if not last:
       
   213         last = first
       
   214     return first, last
       
   215 
       
   216 # Helper to parse a text index into a (line, col) tuple.
       
   217 
       
   218 def get_line_col(index):
       
   219     line, col = map(int, index.split(".")) # Fails on invalid index
       
   220     return line, col