Added 'list_only' option (and modified 'run()' to respect it).
[python/dscho.git] / Mac / Tools / IDE / PyDocSearch.py
blobf975026d087daed957b2298f2d8d62ececff25fb
1 import aetools
2 import Standard_Suite
3 import Required_Suite
4 import WWW_Suite
5 import regex
6 import W
7 import macfs
8 import os
9 import MacPrefs
10 import MacOS
11 import string
13 if hasattr(WWW_Suite, "WWW_Suite"):
14 WWW = WWW_Suite.WWW_Suite
15 else:
16 WWW = WWW_Suite.WorldWideWeb_suite_2c__as_defined_in_Spyglass_spec_2e_
18 class WebBrowser(aetools.TalkTo,
19 Standard_Suite.Standard_Suite,
20 WWW):
22 def openfile(self, path, activate = 1):
23 if activate:
24 self.activate()
25 self.OpenURL("file:///" + string.join(string.split(path,':'), '/'))
27 app = W.getapplication()
29 #SIGNATURE='MSIE' # MS Explorer
30 SIGNATURE='MOSS' # Netscape
32 _titlepat = regex.compile('<title>\([^<]*\)</title>')
34 def sucktitle(path):
35 f = open(path)
36 text = f.read(1024) # assume the title is in the first 1024 bytes
37 f.close()
38 lowertext = string.lower(text)
39 if _titlepat.search(lowertext) > 0:
40 a, b = _titlepat.regs[1]
41 return text[a:b]
42 return path
44 def verifydocpath(docpath):
45 try:
46 tut = os.path.join(docpath, "tut")
47 lib = os.path.join(docpath, "lib")
48 ref = os.path.join(docpath, "ref")
49 for path in [tut, lib, ref]:
50 if not os.path.exists(path):
51 return 0
52 except:
53 return 0
54 return 1
57 class TwoLineList(W.List):
59 LDEF_ID = 468
61 def createlist(self):
62 import List
63 self._calcbounds()
64 self.SetPort()
65 rect = self._bounds
66 rect = rect[0]+1, rect[1]+1, rect[2]-16, rect[3]-1
67 self._list = List.LNew(rect, (0, 0, 1, 0), (0, 28), self.LDEF_ID, self._parentwindow.wid,
68 0, 1, 0, 1)
69 self.set(self.items)
72 _resultscounter = 1
74 class Results:
76 def __init__(self, hits):
77 global _resultscounter
78 hits = map(lambda (path, hits): (sucktitle(path), path, hits), hits)
79 hits.sort()
80 self.hits = hits
81 nicehits = map(
82 lambda (title, path, hits):
83 title + '\r' + string.join(
84 map(lambda (c, p): "%s (%d)" % (p, c), hits), ', '), hits)
85 nicehits.sort()
86 self.w = W.Window((440, 300), "Search results %d" % _resultscounter, minsize = (200, 100))
87 self.w.results = TwoLineList((-1, -1, 1, -14), nicehits, self.listhit)
88 self.w.open()
89 self.w.bind('return', self.listhit)
90 self.w.bind('enter', self.listhit)
91 _resultscounter = _resultscounter + 1
92 self.browser = None
94 def listhit(self, isdbl = 1):
95 if isdbl:
96 for i in self.w.results.getselection():
97 if self.browser is None:
98 self.browser = WebBrowser(SIGNATURE, start = 1)
99 self.browser.openfile(self.hits[i][1])
101 class Status:
103 def __init__(self):
104 self.w = W.Dialog((440, 64), "SearchingÅ ")
105 self.w.searching = W.TextBox((4, 4, -4, 16), "DevDev:PyPyDoc 1.5.1:ext:parseTupleAndKeywords.html")
106 self.w.hits = W.TextBox((4, 24, -4, 16), "Hits: 0")
107 self.w.canceltip = W.TextBox((4, 44, -4, 16), "Type cmd-period (.) to cancel.")
108 self.w.open()
110 def set(self, path, hits):
111 self.w.searching.set(path)
112 self.w.hits.set('Hits: ' + `hits`)
113 app.breathe()
115 def close(self):
116 self.w.close()
119 def match(text, patterns, all):
120 hits = []
121 hitsappend = hits.append
122 stringcount = string.count
123 for pat in patterns:
124 c = stringcount(text, pat)
125 if c > 0:
126 hitsappend((c, pat))
127 elif all:
128 hits[:] = []
129 break
130 hits.sort()
131 hits.reverse()
132 return hits
134 def dosearch(docpath, searchstring, settings):
135 (docpath, kind, case, word, tut, lib, ref, ext, api) = settings
136 books = [(tut, 'tut'), (lib, 'lib'), (ref, 'ref'), (ext, 'ext'), (api, 'api')]
137 if not case:
138 searchstring = string.lower(searchstring)
140 if kind == 1:
141 patterns = string.split(searchstring)
142 all = 1
143 elif kind == 2:
144 patterns = string.split(searchstring)
145 all = 0
146 else:
147 patterns = [searchstring]
148 all = 0 # not relevant
150 ospathjoin = os.path.join
151 stringlower = string.lower
152 status = Status()
153 statusset = status.set
154 _match = match
155 _open = open
156 hits = {}
157 try:
158 MacOS.EnableAppswitch(0)
159 try:
160 for do, name in books:
161 if not do:
162 continue
163 bookpath = ospathjoin(docpath, name)
164 if not os.path.exists(bookpath):
165 continue
166 files = os.listdir(bookpath)
167 for file in files:
168 fullpath = ospathjoin(bookpath, file)
169 if fullpath[-5:] <> '.html':
170 continue
171 statusset(fullpath, len(hits))
172 f = _open(fullpath)
173 text = f.read()
174 if not case:
175 text = stringlower(text)
176 f.close()
177 filehits = _match(text, patterns, all)
178 if filehits:
179 hits[fullpath] = filehits
180 finally:
181 MacOS.EnableAppswitch(-1)
182 status.close()
183 except KeyboardInterrupt:
184 pass
185 hits = hits.items()
186 hits.sort()
187 return hits
190 class PyDocSearch:
192 def __init__(self):
193 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
194 try:
195 (docpath, kind, case, word, tut, lib, ref, ext, api) = prefs.docsearchengine
196 except:
197 (docpath, kind, case, word, tut, lib, ref, ext, api) = prefs.docsearchengine = \
198 ("", 0, 0, 0, 1, 1, 0, 0, 0)
200 if docpath and not verifydocpath(docpath):
201 docpath = ""
203 self.w = W.Window((400, 200), "Search the Python Documentation")
204 self.w.searchtext = W.EditText((10, 10, -100, 20), callback = self.checkbuttons)
205 self.w.searchbutton = W.Button((-90, 12, 80, 16), "Search", self.search)
206 buttons = []
208 gutter = 10
209 width = 130
210 bookstart = width + 2 * gutter
211 self.w.phraseradio = W.RadioButton((10, 38, width, 16), "As a phrase", buttons)
212 self.w.allwordsradio = W.RadioButton((10, 58, width, 16), "All words", buttons)
213 self.w.anywordsradio = W.RadioButton((10, 78, width, 16), "Any word", buttons)
214 self.w.casesens = W.CheckBox((10, 98, width, 16), "Case sensitive")
215 self.w.wholewords = W.CheckBox((10, 118, width, 16), "Whole words")
216 self.w.tutorial = W.CheckBox((bookstart, 38, -10, 16), "Tutorial")
217 self.w.library = W.CheckBox((bookstart, 58, -10, 16), "Library reference")
218 self.w.langueref = W.CheckBox((bookstart, 78, -10, 16), "Lanuage reference manual")
219 self.w.extending = W.CheckBox((bookstart, 98, -10, 16), "Extending & embedding")
220 self.w.api = W.CheckBox((bookstart, 118, -10, 16), "C/C++ API")
222 self.w.setdocfolderbutton = W.Button((10, -30, 80, 16), "Set doc folder", self.setdocpath)
224 if docpath:
225 self.w.setdefaultbutton(self.w.searchbutton)
226 else:
227 self.w.setdefaultbutton(self.w.setdocfolderbutton)
229 self.docpath = docpath
230 if not docpath:
231 docpath = "(please select the Python html documentation folder)"
232 self.w.docfolder = W.TextBox((100, -28, -10, 16), docpath)
234 [self.w.phraseradio, self.w.allwordsradio, self.w.anywordsradio][kind].set(1)
236 self.w.casesens.set(case)
237 self.w.wholewords.set(word)
238 self.w.tutorial.set(tut)
239 self.w.library.set(lib)
240 self.w.langueref.set(ref)
241 self.w.extending.set(ext)
242 self.w.api.set(api)
244 self.w.open()
245 self.w.wholewords.enable(0)
246 self.w.bind('<close>', self.close)
247 self.w.searchbutton.enable(0)
249 def search(self):
250 hits = dosearch(self.docpath, self.w.searchtext.get(), self.getsettings())
251 if hits:
252 Results(hits)
253 elif hasattr(MacOS, 'SysBeep'):
254 MacOS.SysBeep(0)
255 #import PyBrowser
256 #PyBrowser.Browser(hits)
258 def setdocpath(self):
259 fss, ok = macfs.GetDirectory()
260 if ok:
261 docpath = fss.as_pathname()
262 if not verifydocpath(docpath):
263 W.Message("This does not seem to be a Python documentation folder...")
264 else:
265 self.docpath = docpath
266 self.w.docfolder.set(docpath)
267 self.w.setdefaultbutton(self.w.searchbutton)
269 def close(self):
270 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
271 prefs.docsearchengine = self.getsettings()
273 def getsettings(self):
274 radiobuttons = [self.w.phraseradio, self.w.allwordsradio, self.w.anywordsradio]
275 for i in range(3):
276 if radiobuttons[i].get():
277 kind = i
278 break
279 docpath = self.docpath
280 case = self.w.casesens.get()
281 word = self.w.wholewords.get()
282 tut = self.w.tutorial.get()
283 lib = self.w.library.get()
284 ref = self.w.langueref.get()
285 ext = self.w.extending.get()
286 api = self.w.api.get()
287 return (docpath, kind, case, word, tut, lib, ref, ext, api)
289 def checkbuttons(self):
290 self.w.searchbutton.enable(not not self.w.searchtext.get())