codekingpro/portable-devtools
115k
1# SGrepMDI is by Gordon McMillan (gmcm@hypernet.com)2# It does basically what Find In Files does in MSVC with a couple enhancements.3# - It saves any directories in the app's ini file (if you want to get rid4# of them you'll have to edit the file)5# - "Directories" can be directories,6# - semicolon separated lists of "directories",7# - environment variables that evaluate to "directories",8# - registry path names that evaluate to "directories",9# - all of which is recursive, so you can mix them all up.10# - It is MDI, so you can 'nest' greps and return to earlier ones,11# (ie, have multiple results open at the same time)12# - Like FIF, double clicking a line opens an editor and takes you to the line.13# - You can highlight text, right click and start a new grep with the selected14# text as search pattern and same directories etc as before.15# - You can save grep parameters (so you don't lose your hardearned pattern)16# from File|Save17# - You can save grep results by right clicking in the result window.18# Hats off to Mark Hammond for providing an environment where I could cobble19# something like this together in a couple evenings!20 21import glob22import os23import re24 25import win32api26import win32con27import win32ui28from pywin.mfc import dialog, docview, window29 30from . import scriptutils31 32 33def getsubdirs(d):34 dlist = []35 flist = glob.glob(d + "\\*")36 for f in flist:37 if os.path.isdir(f):38 dlist.append(f)39 dlist = dlist + getsubdirs(f)40 return dlist41 42 43class dirpath:44 def __init__(self, str, recurse=0):45 dp = str.split(";")46 dirs = {}47 for d in dp:48 if os.path.isdir(d):49 d = d.lower()50 if d not in dirs:51 dirs[d] = None52 if recurse:53 subdirs = getsubdirs(d)54 for sd in subdirs:55 sd = sd.lower()56 if sd not in dirs:57 dirs[sd] = None58 elif os.path.isfile(d):59 pass60 else:61 x = None62 if d in os.environ:63 x = dirpath(os.environ[d])64 elif d[:5] == "HKEY_":65 keystr = d.split("\\")66 try:67 root = eval("win32con." + keystr[0])68 except:69 win32ui.MessageBox(70 "Can't interpret registry key name '%s'" % keystr[0]71 )72 try:73 subkey = "\\".join(keystr[1:])74 val = win32api.RegQueryValue(root, subkey)75 if val:76 x = dirpath(val)77 else:78 win32ui.MessageBox(79 "Registry path '%s' did not return a path entry" % d80 )81 except:82 win32ui.MessageBox(83 "Can't interpret registry key value: %s" % keystr[1:]84 )85 else:86 win32ui.MessageBox("Directory '%s' not found" % d)87 if x:88 for xd in x:89 if xd not in dirs:90 dirs[xd] = None91 if recurse:92 subdirs = getsubdirs(xd)93 for sd in subdirs:94 sd = sd.lower()95 if sd not in dirs:96 dirs[sd] = None97 self.dirs = []98 for d in list(dirs.keys()):99 self.dirs.append(d)100 101 def __getitem__(self, key):102 return self.dirs[key]103 104 def __len__(self):105 return len(self.dirs)106 107 def __setitem__(self, key, value):108 self.dirs[key] = value109 110 def __delitem__(self, key):111 del self.dirs[key]112 113 def __getslice__(self, lo, hi):114 return self.dirs[lo:hi]115 116 def __setslice__(self, lo, hi, seq):117 self.dirs[lo:hi] = seq118 119 def __delslice__(self, lo, hi):120 del self.dirs[lo:hi]121 122 def __add__(self, other):123 if type(other) == type(self) or type(other) == type([]):124 return self.dirs + other.dirs125 126 def __radd__(self, other):127 if type(other) == type(self) or type(other) == type([]):128 return other.dirs + self.dirs129 130 131# Group(1) is the filename, group(2) is the lineno.132# regexGrepResult=regex.compile("^\\([a-zA-Z]:.*\\)(\\([0-9]+\\))")133 134regexGrep = re.compile(r"^([a-zA-Z]:[^(]*)\(([0-9]+)\)")135 136# these are the atom numbers defined by Windows for basic dialog controls137 138BUTTON = 0x80139EDIT = 0x81140STATIC = 0x82141LISTBOX = 0x83142SCROLLBAR = 0x84143COMBOBOX = 0x85144 145 146class GrepTemplate(docview.RichEditDocTemplate):147 def __init__(self):148 docview.RichEditDocTemplate.__init__(149 self, win32ui.IDR_TEXTTYPE, GrepDocument, GrepFrame, GrepView150 )151 self.SetDocStrings("\nGrep\nGrep\nGrep params (*.grep)\n.grep\n\n\n")152 win32ui.GetApp().AddDocTemplate(self)153 self.docparams = None154 155 def MatchDocType(self, fileName, fileType):156 doc = self.FindOpenDocument(fileName)157 if doc:158 return doc159 ext = os.path.splitext(fileName)[1].lower()160 if ext == ".grep":161 return win32ui.CDocTemplate_Confidence_yesAttemptNative162 return win32ui.CDocTemplate_Confidence_noAttempt163 164 def setParams(self, params):165 self.docparams = params166 167 def readParams(self):168 tmp = self.docparams169 self.docparams = None170 return tmp171 172 173class GrepFrame(window.MDIChildWnd):174 # The template and doc params will one day be removed.175 def __init__(self, wnd=None):176 window.MDIChildWnd.__init__(self, wnd)177 178 179class GrepDocument(docview.RichEditDoc):180 def __init__(self, template):181 docview.RichEditDoc.__init__(self, template)182 self.dirpattern = ""183 self.filpattern = ""184 self.greppattern = ""185 self.casesensitive = 1186 self.recurse = 1187 self.verbose = 0188 189 def OnOpenDocument(self, fnm):190 # this bizarre stuff with params is so right clicking in a result window191 # and starting a new grep can communicate the default parameters to the192 # new grep.193 try:194 params = open(fnm, "r").read()195 except:196 params = None197 self.setInitParams(params)198 return self.OnNewDocument()199 200 def OnCloseDocument(self):201 try:202 win32ui.GetApp().DeleteIdleHandler(self.SearchFile)203 except:204 pass205 return self._obj_.OnCloseDocument()206 207 def saveInitParams(self):208 # Only save the flags, not the text boxes.209 paramstr = "\t%s\t\t%d\t%d" % (210 self.filpattern,211 self.casesensitive,212 self.recurse,213 )214 win32ui.WriteProfileVal("Grep", "Params", paramstr)215 216 def setInitParams(self, paramstr):217 if paramstr is None:218 paramstr = win32ui.GetProfileVal("Grep", "Params", "\t\t\t1\t0\t0")219 params = paramstr.split("\t")220 if len(params) < 3:221 params = params + [""] * (3 - len(params))222 if len(params) < 6:223 params = params + [0] * (6 - len(params))224 self.dirpattern = params[0]225 self.filpattern = params[1]226 self.greppattern = params[2]227 self.casesensitive = int(params[3])228 self.recurse = int(params[4])229 self.verbose = int(params[5])230 # setup some reasonable defaults.231 if not self.dirpattern:232 try:233 editor = win32ui.GetMainFrame().MDIGetActive()[0].GetEditorView()234 self.dirpattern = os.path.abspath(235 os.path.dirname(editor.GetDocument().GetPathName())236 )237 except (AttributeError, win32ui.error):238 self.dirpattern = os.getcwd()239 if not self.filpattern:240 self.filpattern = "*.py"241 242 def OnNewDocument(self):243 if self.dirpattern == "":244 self.setInitParams(greptemplate.readParams())245 d = GrepDialog(246 self.dirpattern,247 self.filpattern,248 self.greppattern,249 self.casesensitive,250 self.recurse,251 self.verbose,252 )253 if d.DoModal() == win32con.IDOK:254 self.dirpattern = d["dirpattern"]255 self.filpattern = d["filpattern"]256 self.greppattern = d["greppattern"]257 self.casesensitive = d["casesensitive"]258 self.recurse = d["recursive"]259 self.verbose = d["verbose"]260 self.doSearch()261 self.saveInitParams()262 return 1263 return 0 # cancelled - return zero to stop frame creation.264 265 def doSearch(self):266 self.dp = dirpath(self.dirpattern, self.recurse)267 self.SetTitle("Grep for %s in %s" % (self.greppattern, self.filpattern))268 # self.text = []269 self.GetFirstView().Append("#Search " + self.dirpattern + "\n")270 if self.verbose:271 self.GetFirstView().Append("# =" + repr(self.dp.dirs) + "\n")272 self.GetFirstView().Append("# Files " + self.filpattern + "\n")273 self.GetFirstView().Append("# For " + self.greppattern + "\n")274 self.fplist = self.filpattern.split(";")275 if self.casesensitive:276 self.pat = re.compile(self.greppattern)277 else:278 self.pat = re.compile(self.greppattern, re.IGNORECASE)279 win32ui.SetStatusText("Searching. Please wait...", 0)280 self.dpndx = self.fpndx = 0281 self.fndx = -1282 if not self.dp:283 self.GetFirstView().Append(284 "# ERROR: '%s' does not resolve to any search locations"285 % self.dirpattern286 )287 self.SetModifiedFlag(0)288 else:289 self.flist = glob.glob(self.dp[0] + "\\" + self.fplist[0])290 win32ui.GetApp().AddIdleHandler(self.SearchFile)291 292 def SearchFile(self, handler, count):293 self.fndx = self.fndx + 1294 if self.fndx < len(self.flist):295 f = self.flist[self.fndx]296 if self.verbose:297 self.GetFirstView().Append("# .." + f + "\n")298 # Directories may match the file type pattern, and files may be removed299 # while grep is running300 if os.path.isfile(f):301 win32ui.SetStatusText("Searching " + f, 0)302 lines = open(f, "r").readlines()303 for i in range(len(lines)):304 line = lines[i]305 if self.pat.search(line) != None:306 self.GetFirstView().Append(f + "(" + repr(i + 1) + ") " + line)307 else:308 self.fndx = -1309 self.fpndx = self.fpndx + 1310 if self.fpndx < len(self.fplist):311 self.flist = glob.glob(312 self.dp[self.dpndx] + "\\" + self.fplist[self.fpndx]313 )314 else:315 self.fpndx = 0316 self.dpndx = self.dpndx + 1317 if self.dpndx < len(self.dp):318 self.flist = glob.glob(319 self.dp[self.dpndx] + "\\" + self.fplist[self.fpndx]320 )321 else:322 win32ui.SetStatusText("Search complete.", 0)323 self.SetModifiedFlag(0) # default to not modified.324 try:325 win32ui.GetApp().DeleteIdleHandler(self.SearchFile)326 except:327 pass328 return 0329 return 1330 331 def GetParams(self):332 return (333 self.dirpattern334 + "\t"335 + self.filpattern336 + "\t"337 + self.greppattern338 + "\t"339 + repr(self.casesensitive)340 + "\t"341 + repr(self.recurse)342 + "\t"343 + repr(self.verbose)344 )345 346 def OnSaveDocument(self, filename):347 # print 'OnSaveDocument() filename=',filename348 savefile = open(filename, "wb")349 txt = self.GetParams() + "\n"350 # print 'writing',txt351 savefile.write(txt)352 savefile.close()353 self.SetModifiedFlag(0)354 return 1355 356 357ID_OPEN_FILE = 0xE400358ID_GREP = 0xE401359ID_SAVERESULTS = 0x402360ID_TRYAGAIN = 0x403361 362 363class GrepView(docview.RichEditView):364 def __init__(self, doc):365 docview.RichEditView.__init__(self, doc)366 self.SetWordWrap(win32ui.CRichEditView_WrapNone)367 self.HookHandlers()368 369 def OnInitialUpdate(self):370 rc = self._obj_.OnInitialUpdate()371 format = (-402653169, 0, 200, 0, 0, 0, 49, "Courier New")372 self.SetDefaultCharFormat(format)373 return rc374 375 def HookHandlers(self):376 self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN)377 self.HookCommand(self.OnCmdOpenFile, ID_OPEN_FILE)378 self.HookCommand(self.OnCmdGrep, ID_GREP)379 self.HookCommand(self.OnCmdSave, ID_SAVERESULTS)380 self.HookCommand(self.OnTryAgain, ID_TRYAGAIN)381 self.HookMessage(self.OnLDblClick, win32con.WM_LBUTTONDBLCLK)382 383 def OnLDblClick(self, params):384 line = self.GetLine()385 regexGrepResult = regexGrep.match(line)386 if regexGrepResult:387 fname = regexGrepResult.group(1)388 line = int(regexGrepResult.group(2))389 scriptutils.JumpToDocument(fname, line)390 return 0 # dont pass on391 return 1 # pass it on by default.392 393 def OnRClick(self, params):394 menu = win32ui.CreatePopupMenu()395 flags = win32con.MF_STRING | win32con.MF_ENABLED396 lineno = self._obj_.LineFromChar(-1) # selection or current line397 line = self._obj_.GetLine(lineno)398 regexGrepResult = regexGrep.match(line)399 if regexGrepResult:400 self.fnm = regexGrepResult.group(1)401 self.lnnum = int(regexGrepResult.group(2))402 menu.AppendMenu(flags, ID_OPEN_FILE, "&Open " + self.fnm)403 menu.AppendMenu(win32con.MF_SEPARATOR)404 menu.AppendMenu(flags, ID_TRYAGAIN, "&Try Again")405 charstart, charend = self._obj_.GetSel()406 if charstart != charend:407 linestart = self._obj_.LineIndex(lineno)408 self.sel = line[charstart - linestart : charend - linestart]409 menu.AppendMenu(flags, ID_GREP, "&Grep for " + self.sel)410 menu.AppendMenu(win32con.MF_SEPARATOR)411 menu.AppendMenu(flags, win32ui.ID_EDIT_CUT, "Cu&t")412 menu.AppendMenu(flags, win32ui.ID_EDIT_COPY, "&Copy")413 menu.AppendMenu(flags, win32ui.ID_EDIT_PASTE, "&Paste")414 menu.AppendMenu(flags, win32con.MF_SEPARATOR)415 menu.AppendMenu(flags, win32ui.ID_EDIT_SELECT_ALL, "&Select all")416 menu.AppendMenu(flags, win32con.MF_SEPARATOR)417 menu.AppendMenu(flags, ID_SAVERESULTS, "Sa&ve results")418 menu.TrackPopupMenu(params[5])419 return 0420 421 def OnCmdOpenFile(self, cmd, code):422 doc = win32ui.GetApp().OpenDocumentFile(self.fnm)423 if doc:424 vw = doc.GetFirstView()425 # hope you have an editor that implements GotoLine()!426 try:427 vw.GotoLine(int(self.lnnum))428 except:429 pass430 return 0431 432 def OnCmdGrep(self, cmd, code):433 if code != 0:434 return 1435 curparamsstr = self.GetDocument().GetParams()436 params = curparamsstr.split("\t")437 params[2] = self.sel438 greptemplate.setParams("\t".join(params))439 greptemplate.OpenDocumentFile()440 return 0441 442 def OnTryAgain(self, cmd, code):443 if code != 0:444 return 1445 greptemplate.setParams(self.GetDocument().GetParams())446 greptemplate.OpenDocumentFile()447 return 0448 449 def OnCmdSave(self, cmd, code):450 if code != 0:451 return 1452 flags = win32con.OFN_OVERWRITEPROMPT453 dlg = win32ui.CreateFileDialog(454 0, None, None, flags, "Text Files (*.txt)|*.txt||", self455 )456 dlg.SetOFNTitle("Save Results As")457 if dlg.DoModal() == win32con.IDOK:458 pn = dlg.GetPathName()459 self._obj_.SaveTextFile(pn)460 return 0461 462 def Append(self, strng):463 numlines = self.GetLineCount()464 endpos = self.LineIndex(numlines - 1) + len(self.GetLine(numlines - 1))465 self.SetSel(endpos, endpos)466 self.ReplaceSel(strng)467 468 469class GrepDialog(dialog.Dialog):470 def __init__(self, dp, fp, gp, cs, r, v):471 style = (472 win32con.DS_MODALFRAME473 | win32con.WS_POPUP474 | win32con.WS_VISIBLE475 | win32con.WS_CAPTION476 | win32con.WS_SYSMENU477 | win32con.DS_SETFONT478 )479 CS = win32con.WS_CHILD | win32con.WS_VISIBLE480 tmp = [481 ["Grep", (0, 0, 210, 90), style, None, (8, "MS Sans Serif")],482 ]483 tmp.append([STATIC, "Grep For:", -1, (7, 7, 50, 9), CS])484 tmp.append(485 [486 EDIT,487 gp,488 101,489 (52, 7, 144, 11),490 CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,491 ]492 )493 tmp.append([STATIC, "Directories:", -1, (7, 20, 50, 9), CS])494 tmp.append(495 [496 EDIT,497 dp,498 102,499 (52, 20, 128, 11),500 CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,501 ]502 )503 tmp.append(504 [505 BUTTON,506 "...",507 110,508 (182, 20, 16, 11),509 CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,510 ]511 )512 tmp.append([STATIC, "File types:", -1, (7, 33, 50, 9), CS])513 tmp.append(514 [515 EDIT,516 fp,517 103,518 (52, 33, 128, 11),519 CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,520 ]521 )522 tmp.append(523 [524 BUTTON,525 "...",526 111,527 (182, 33, 16, 11),528 CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,529 ]530 )531 tmp.append(532 [533 BUTTON,534 "Case sensitive",535 104,536 (7, 45, 72, 9),537 CS538 | win32con.BS_AUTOCHECKBOX539 | win32con.BS_LEFTTEXT540 | win32con.WS_TABSTOP,541 ]542 )543 tmp.append(544 [545 BUTTON,546 "Subdirectories",547 105,548 (7, 56, 72, 9),549 CS550 | win32con.BS_AUTOCHECKBOX551 | win32con.BS_LEFTTEXT552 | win32con.WS_TABSTOP,553 ]554 )555 tmp.append(556 [557 BUTTON,558 "Verbose",559 106,560 (7, 67, 72, 9),561 CS562 | win32con.BS_AUTOCHECKBOX563 | win32con.BS_LEFTTEXT564 | win32con.WS_TABSTOP,565 ]566 )567 tmp.append(568 [569 BUTTON,570 "OK",571 win32con.IDOK,572 (166, 53, 32, 12),573 CS | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP,574 ]575 )576 tmp.append(577 [578 BUTTON,579 "Cancel",580 win32con.IDCANCEL,581 (166, 67, 32, 12),582 CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,583 ]584 )585 dialog.Dialog.__init__(self, tmp)586 self.AddDDX(101, "greppattern")587 self.AddDDX(102, "dirpattern")588 self.AddDDX(103, "filpattern")589 self.AddDDX(104, "casesensitive")590 self.AddDDX(105, "recursive")591 self.AddDDX(106, "verbose")592 self._obj_.data["greppattern"] = gp593 self._obj_.data["dirpattern"] = dp594 self._obj_.data["filpattern"] = fp595 self._obj_.data["casesensitive"] = cs596 self._obj_.data["recursive"] = r597 self._obj_.data["verbose"] = v598 self.HookCommand(self.OnMoreDirectories, 110)599 self.HookCommand(self.OnMoreFiles, 111)600 601 def OnMoreDirectories(self, cmd, code):602 if code != 0:603 return 1604 self.getMore("Grep\\Directories", "dirpattern")605 606 def OnMoreFiles(self, cmd, code):607 if code != 0:608 return 1609 self.getMore("Grep\\File Types", "filpattern")610 611 def getMore(self, section, key):612 self.UpdateData(1)613 # get the items out of the ini file614 ini = win32ui.GetProfileFileName()615 secitems = win32api.GetProfileSection(section, ini)616 items = []617 for secitem in secitems:618 items.append(secitem.split("=")[1])619 dlg = GrepParamsDialog(items)620 if dlg.DoModal() == win32con.IDOK:621 itemstr = ";".join(dlg.getItems())622 self._obj_.data[key] = itemstr623 # update the ini file with dlg.getNew()624 i = 0625 newitems = dlg.getNew()626 if newitems:627 items = items + newitems628 for item in items:629 win32api.WriteProfileVal(section, repr(i), item, ini)630 i = i + 1631 self.UpdateData(0)632 633 def OnOK(self):634 self.UpdateData(1)635 for id, name in (636 (101, "greppattern"),637 (102, "dirpattern"),638 (103, "filpattern"),639 ):640 if not self[name]:641 self.GetDlgItem(id).SetFocus()642 win32api.MessageBeep()643 win32ui.SetStatusText("Please enter a value")644 return645 self._obj_.OnOK()646 647 648class GrepParamsDialog(dialog.Dialog):649 def __init__(self, items):650 self.items = items651 self.newitems = []652 style = (653 win32con.DS_MODALFRAME654 | win32con.WS_POPUP655 | win32con.WS_VISIBLE656 | win32con.WS_CAPTION657 | win32con.WS_SYSMENU658 | win32con.DS_SETFONT659 )660 CS = win32con.WS_CHILD | win32con.WS_VISIBLE661 tmp = [662 ["Grep Parameters", (0, 0, 205, 100), style, None, (8, "MS Sans Serif")],663 ]664 tmp.append(665 [666 LISTBOX,667 "",668 107,669 (7, 7, 150, 72),670 CS671 | win32con.LBS_MULTIPLESEL672 | win32con.LBS_STANDARD673 | win32con.LBS_HASSTRINGS674 | win32con.WS_TABSTOP675 | win32con.LBS_NOTIFY,676 ]677 )678 tmp.append(679 [680 BUTTON,681 "OK",682 win32con.IDOK,683 (167, 7, 32, 12),684 CS | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP,685 ]686 )687 tmp.append(688 [689 BUTTON,690 "Cancel",691 win32con.IDCANCEL,692 (167, 23, 32, 12),693 CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,694 ]695 )696 tmp.append([STATIC, "New:", -1, (2, 83, 15, 12), CS])697 tmp.append(698 [699 EDIT,700 "",701 108,702 (18, 83, 139, 12),703 CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,704 ]705 )706 tmp.append(707 [708 BUTTON,709 "Add",710 109,711 (167, 83, 32, 12),712 CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,713 ]714 )715 dialog.Dialog.__init__(self, tmp)716 self.HookCommand(self.OnAddItem, 109)717 self.HookCommand(self.OnListDoubleClick, 107)718 719 def OnInitDialog(self):720 lb = self.GetDlgItem(107)721 for item in self.items:722 lb.AddString(item)723 return self._obj_.OnInitDialog()724 725 def OnAddItem(self, cmd, code):726 if code != 0:727 return 1728 eb = self.GetDlgItem(108)729 item = eb.GetLine(0)730 self.newitems.append(item)731 lb = self.GetDlgItem(107)732 i = lb.AddString(item)733 lb.SetSel(i, 1)734 return 1735 736 def OnListDoubleClick(self, cmd, code):737 if code == win32con.LBN_DBLCLK:738 self.OnOK()739 return 1740 741 def OnOK(self):742 lb = self.GetDlgItem(107)743 self.selections = lb.GetSelTextItems()744 self._obj_.OnOK()745 746 def getItems(self):747 return self.selections748 749 def getNew(self):750 return self.newitems751 752 753try:754 win32ui.GetApp().RemoveDocTemplate(greptemplate)755except NameError:756 pass757 758greptemplate = GrepTemplate()759 