AIM-PIbd-32-Kurbanova-A-A/aimenv/Lib/site-packages/pythonwin/pywin/framework/sgrepmdi.py
2024-10-02 22:15:59 +04:00

759 lines
24 KiB
Python

# SGrepMDI is by Gordon McMillan (gmcm@hypernet.com)
# It does basically what Find In Files does in MSVC with a couple enhancements.
# - It saves any directories in the app's ini file (if you want to get rid
# of them you'll have to edit the file)
# - "Directories" can be directories,
# - semicolon separated lists of "directories",
# - environment variables that evaluate to "directories",
# - registry path names that evaluate to "directories",
# - all of which is recursive, so you can mix them all up.
# - It is MDI, so you can 'nest' greps and return to earlier ones,
# (ie, have multiple results open at the same time)
# - Like FIF, double clicking a line opens an editor and takes you to the line.
# - You can highlight text, right click and start a new grep with the selected
# text as search pattern and same directories etc as before.
# - You can save grep parameters (so you don't lose your hardearned pattern)
# from File|Save
# - You can save grep results by right clicking in the result window.
# Hats off to Mark Hammond for providing an environment where I could cobble
# something like this together in a couple evenings!
import glob
import os
import re
import win32api
import win32con
import win32ui
from pywin.mfc import dialog, docview, window
from . import scriptutils
def getsubdirs(d):
dlist = []
flist = glob.glob(d + "\\*")
for f in flist:
if os.path.isdir(f):
dlist.append(f)
dlist = dlist + getsubdirs(f)
return dlist
class dirpath:
def __init__(self, str, recurse=0):
dp = str.split(";")
dirs = {}
for d in dp:
if os.path.isdir(d):
d = d.lower()
if d not in dirs:
dirs[d] = None
if recurse:
subdirs = getsubdirs(d)
for sd in subdirs:
sd = sd.lower()
if sd not in dirs:
dirs[sd] = None
elif os.path.isfile(d):
pass
else:
x = None
if d in os.environ:
x = dirpath(os.environ[d])
elif d[:5] == "HKEY_":
keystr = d.split("\\")
try:
root = eval("win32con." + keystr[0])
except:
win32ui.MessageBox(
"Can't interpret registry key name '%s'" % keystr[0]
)
try:
subkey = "\\".join(keystr[1:])
val = win32api.RegQueryValue(root, subkey)
if val:
x = dirpath(val)
else:
win32ui.MessageBox(
"Registry path '%s' did not return a path entry" % d
)
except:
win32ui.MessageBox(
"Can't interpret registry key value: %s" % keystr[1:]
)
else:
win32ui.MessageBox("Directory '%s' not found" % d)
if x:
for xd in x:
if xd not in dirs:
dirs[xd] = None
if recurse:
subdirs = getsubdirs(xd)
for sd in subdirs:
sd = sd.lower()
if sd not in dirs:
dirs[sd] = None
self.dirs = []
for d in list(dirs.keys()):
self.dirs.append(d)
def __getitem__(self, key):
return self.dirs[key]
def __len__(self):
return len(self.dirs)
def __setitem__(self, key, value):
self.dirs[key] = value
def __delitem__(self, key):
del self.dirs[key]
def __getslice__(self, lo, hi):
return self.dirs[lo:hi]
def __setslice__(self, lo, hi, seq):
self.dirs[lo:hi] = seq
def __delslice__(self, lo, hi):
del self.dirs[lo:hi]
def __add__(self, other):
if type(other) == type(self) or type(other) == type([]):
return self.dirs + other.dirs
def __radd__(self, other):
if type(other) == type(self) or type(other) == type([]):
return other.dirs + self.dirs
# Group(1) is the filename, group(2) is the lineno.
# regexGrepResult=regex.compile("^\\([a-zA-Z]:.*\\)(\\([0-9]+\\))")
regexGrep = re.compile(r"^([a-zA-Z]:[^(]*)\(([0-9]+)\)")
# these are the atom numbers defined by Windows for basic dialog controls
BUTTON = 0x80
EDIT = 0x81
STATIC = 0x82
LISTBOX = 0x83
SCROLLBAR = 0x84
COMBOBOX = 0x85
class GrepTemplate(docview.RichEditDocTemplate):
def __init__(self):
docview.RichEditDocTemplate.__init__(
self, win32ui.IDR_TEXTTYPE, GrepDocument, GrepFrame, GrepView
)
self.SetDocStrings("\nGrep\nGrep\nGrep params (*.grep)\n.grep\n\n\n")
win32ui.GetApp().AddDocTemplate(self)
self.docparams = None
def MatchDocType(self, fileName, fileType):
doc = self.FindOpenDocument(fileName)
if doc:
return doc
ext = os.path.splitext(fileName)[1].lower()
if ext == ".grep":
return win32ui.CDocTemplate_Confidence_yesAttemptNative
return win32ui.CDocTemplate_Confidence_noAttempt
def setParams(self, params):
self.docparams = params
def readParams(self):
tmp = self.docparams
self.docparams = None
return tmp
class GrepFrame(window.MDIChildWnd):
# The template and doc params will one day be removed.
def __init__(self, wnd=None):
window.MDIChildWnd.__init__(self, wnd)
class GrepDocument(docview.RichEditDoc):
def __init__(self, template):
docview.RichEditDoc.__init__(self, template)
self.dirpattern = ""
self.filpattern = ""
self.greppattern = ""
self.casesensitive = 1
self.recurse = 1
self.verbose = 0
def OnOpenDocument(self, fnm):
# this bizarre stuff with params is so right clicking in a result window
# and starting a new grep can communicate the default parameters to the
# new grep.
try:
params = open(fnm, "r").read()
except:
params = None
self.setInitParams(params)
return self.OnNewDocument()
def OnCloseDocument(self):
try:
win32ui.GetApp().DeleteIdleHandler(self.SearchFile)
except:
pass
return self._obj_.OnCloseDocument()
def saveInitParams(self):
# Only save the flags, not the text boxes.
paramstr = "\t%s\t\t%d\t%d" % (
self.filpattern,
self.casesensitive,
self.recurse,
)
win32ui.WriteProfileVal("Grep", "Params", paramstr)
def setInitParams(self, paramstr):
if paramstr is None:
paramstr = win32ui.GetProfileVal("Grep", "Params", "\t\t\t1\t0\t0")
params = paramstr.split("\t")
if len(params) < 3:
params = params + [""] * (3 - len(params))
if len(params) < 6:
params = params + [0] * (6 - len(params))
self.dirpattern = params[0]
self.filpattern = params[1]
self.greppattern = params[2]
self.casesensitive = int(params[3])
self.recurse = int(params[4])
self.verbose = int(params[5])
# setup some reasonable defaults.
if not self.dirpattern:
try:
editor = win32ui.GetMainFrame().MDIGetActive()[0].GetEditorView()
self.dirpattern = os.path.abspath(
os.path.dirname(editor.GetDocument().GetPathName())
)
except (AttributeError, win32ui.error):
self.dirpattern = os.getcwd()
if not self.filpattern:
self.filpattern = "*.py"
def OnNewDocument(self):
if self.dirpattern == "":
self.setInitParams(greptemplate.readParams())
d = GrepDialog(
self.dirpattern,
self.filpattern,
self.greppattern,
self.casesensitive,
self.recurse,
self.verbose,
)
if d.DoModal() == win32con.IDOK:
self.dirpattern = d["dirpattern"]
self.filpattern = d["filpattern"]
self.greppattern = d["greppattern"]
self.casesensitive = d["casesensitive"]
self.recurse = d["recursive"]
self.verbose = d["verbose"]
self.doSearch()
self.saveInitParams()
return 1
return 0 # cancelled - return zero to stop frame creation.
def doSearch(self):
self.dp = dirpath(self.dirpattern, self.recurse)
self.SetTitle("Grep for %s in %s" % (self.greppattern, self.filpattern))
# self.text = []
self.GetFirstView().Append("#Search " + self.dirpattern + "\n")
if self.verbose:
self.GetFirstView().Append("# =" + repr(self.dp.dirs) + "\n")
self.GetFirstView().Append("# Files " + self.filpattern + "\n")
self.GetFirstView().Append("# For " + self.greppattern + "\n")
self.fplist = self.filpattern.split(";")
if self.casesensitive:
self.pat = re.compile(self.greppattern)
else:
self.pat = re.compile(self.greppattern, re.IGNORECASE)
win32ui.SetStatusText("Searching. Please wait...", 0)
self.dpndx = self.fpndx = 0
self.fndx = -1
if not self.dp:
self.GetFirstView().Append(
"# ERROR: '%s' does not resolve to any search locations"
% self.dirpattern
)
self.SetModifiedFlag(0)
else:
self.flist = glob.glob(self.dp[0] + "\\" + self.fplist[0])
win32ui.GetApp().AddIdleHandler(self.SearchFile)
def SearchFile(self, handler, count):
self.fndx = self.fndx + 1
if self.fndx < len(self.flist):
f = self.flist[self.fndx]
if self.verbose:
self.GetFirstView().Append("# .." + f + "\n")
# Directories may match the file type pattern, and files may be removed
# while grep is running
if os.path.isfile(f):
win32ui.SetStatusText("Searching " + f, 0)
lines = open(f, "r").readlines()
for i in range(len(lines)):
line = lines[i]
if self.pat.search(line) != None:
self.GetFirstView().Append(f + "(" + repr(i + 1) + ") " + line)
else:
self.fndx = -1
self.fpndx = self.fpndx + 1
if self.fpndx < len(self.fplist):
self.flist = glob.glob(
self.dp[self.dpndx] + "\\" + self.fplist[self.fpndx]
)
else:
self.fpndx = 0
self.dpndx = self.dpndx + 1
if self.dpndx < len(self.dp):
self.flist = glob.glob(
self.dp[self.dpndx] + "\\" + self.fplist[self.fpndx]
)
else:
win32ui.SetStatusText("Search complete.", 0)
self.SetModifiedFlag(0) # default to not modified.
try:
win32ui.GetApp().DeleteIdleHandler(self.SearchFile)
except:
pass
return 0
return 1
def GetParams(self):
return (
self.dirpattern
+ "\t"
+ self.filpattern
+ "\t"
+ self.greppattern
+ "\t"
+ repr(self.casesensitive)
+ "\t"
+ repr(self.recurse)
+ "\t"
+ repr(self.verbose)
)
def OnSaveDocument(self, filename):
# print 'OnSaveDocument() filename=',filename
savefile = open(filename, "wb")
txt = self.GetParams() + "\n"
# print 'writing',txt
savefile.write(txt)
savefile.close()
self.SetModifiedFlag(0)
return 1
ID_OPEN_FILE = 0xE400
ID_GREP = 0xE401
ID_SAVERESULTS = 0x402
ID_TRYAGAIN = 0x403
class GrepView(docview.RichEditView):
def __init__(self, doc):
docview.RichEditView.__init__(self, doc)
self.SetWordWrap(win32ui.CRichEditView_WrapNone)
self.HookHandlers()
def OnInitialUpdate(self):
rc = self._obj_.OnInitialUpdate()
format = (-402653169, 0, 200, 0, 0, 0, 49, "Courier New")
self.SetDefaultCharFormat(format)
return rc
def HookHandlers(self):
self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN)
self.HookCommand(self.OnCmdOpenFile, ID_OPEN_FILE)
self.HookCommand(self.OnCmdGrep, ID_GREP)
self.HookCommand(self.OnCmdSave, ID_SAVERESULTS)
self.HookCommand(self.OnTryAgain, ID_TRYAGAIN)
self.HookMessage(self.OnLDblClick, win32con.WM_LBUTTONDBLCLK)
def OnLDblClick(self, params):
line = self.GetLine()
regexGrepResult = regexGrep.match(line)
if regexGrepResult:
fname = regexGrepResult.group(1)
line = int(regexGrepResult.group(2))
scriptutils.JumpToDocument(fname, line)
return 0 # dont pass on
return 1 # pass it on by default.
def OnRClick(self, params):
menu = win32ui.CreatePopupMenu()
flags = win32con.MF_STRING | win32con.MF_ENABLED
lineno = self._obj_.LineFromChar(-1) # selection or current line
line = self._obj_.GetLine(lineno)
regexGrepResult = regexGrep.match(line)
if regexGrepResult:
self.fnm = regexGrepResult.group(1)
self.lnnum = int(regexGrepResult.group(2))
menu.AppendMenu(flags, ID_OPEN_FILE, "&Open " + self.fnm)
menu.AppendMenu(win32con.MF_SEPARATOR)
menu.AppendMenu(flags, ID_TRYAGAIN, "&Try Again")
charstart, charend = self._obj_.GetSel()
if charstart != charend:
linestart = self._obj_.LineIndex(lineno)
self.sel = line[charstart - linestart : charend - linestart]
menu.AppendMenu(flags, ID_GREP, "&Grep for " + self.sel)
menu.AppendMenu(win32con.MF_SEPARATOR)
menu.AppendMenu(flags, win32ui.ID_EDIT_CUT, "Cu&t")
menu.AppendMenu(flags, win32ui.ID_EDIT_COPY, "&Copy")
menu.AppendMenu(flags, win32ui.ID_EDIT_PASTE, "&Paste")
menu.AppendMenu(flags, win32con.MF_SEPARATOR)
menu.AppendMenu(flags, win32ui.ID_EDIT_SELECT_ALL, "&Select all")
menu.AppendMenu(flags, win32con.MF_SEPARATOR)
menu.AppendMenu(flags, ID_SAVERESULTS, "Sa&ve results")
menu.TrackPopupMenu(params[5])
return 0
def OnCmdOpenFile(self, cmd, code):
doc = win32ui.GetApp().OpenDocumentFile(self.fnm)
if doc:
vw = doc.GetFirstView()
# hope you have an editor that implements GotoLine()!
try:
vw.GotoLine(int(self.lnnum))
except:
pass
return 0
def OnCmdGrep(self, cmd, code):
if code != 0:
return 1
curparamsstr = self.GetDocument().GetParams()
params = curparamsstr.split("\t")
params[2] = self.sel
greptemplate.setParams("\t".join(params))
greptemplate.OpenDocumentFile()
return 0
def OnTryAgain(self, cmd, code):
if code != 0:
return 1
greptemplate.setParams(self.GetDocument().GetParams())
greptemplate.OpenDocumentFile()
return 0
def OnCmdSave(self, cmd, code):
if code != 0:
return 1
flags = win32con.OFN_OVERWRITEPROMPT
dlg = win32ui.CreateFileDialog(
0, None, None, flags, "Text Files (*.txt)|*.txt||", self
)
dlg.SetOFNTitle("Save Results As")
if dlg.DoModal() == win32con.IDOK:
pn = dlg.GetPathName()
self._obj_.SaveTextFile(pn)
return 0
def Append(self, strng):
numlines = self.GetLineCount()
endpos = self.LineIndex(numlines - 1) + len(self.GetLine(numlines - 1))
self.SetSel(endpos, endpos)
self.ReplaceSel(strng)
class GrepDialog(dialog.Dialog):
def __init__(self, dp, fp, gp, cs, r, v):
style = (
win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
CS = win32con.WS_CHILD | win32con.WS_VISIBLE
tmp = [
["Grep", (0, 0, 210, 90), style, None, (8, "MS Sans Serif")],
]
tmp.append([STATIC, "Grep For:", -1, (7, 7, 50, 9), CS])
tmp.append(
[
EDIT,
gp,
101,
(52, 7, 144, 11),
CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,
]
)
tmp.append([STATIC, "Directories:", -1, (7, 20, 50, 9), CS])
tmp.append(
[
EDIT,
dp,
102,
(52, 20, 128, 11),
CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,
]
)
tmp.append(
[
BUTTON,
"...",
110,
(182, 20, 16, 11),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
tmp.append([STATIC, "File types:", -1, (7, 33, 50, 9), CS])
tmp.append(
[
EDIT,
fp,
103,
(52, 33, 128, 11),
CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,
]
)
tmp.append(
[
BUTTON,
"...",
111,
(182, 33, 16, 11),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
tmp.append(
[
BUTTON,
"Case sensitive",
104,
(7, 45, 72, 9),
CS
| win32con.BS_AUTOCHECKBOX
| win32con.BS_LEFTTEXT
| win32con.WS_TABSTOP,
]
)
tmp.append(
[
BUTTON,
"Subdirectories",
105,
(7, 56, 72, 9),
CS
| win32con.BS_AUTOCHECKBOX
| win32con.BS_LEFTTEXT
| win32con.WS_TABSTOP,
]
)
tmp.append(
[
BUTTON,
"Verbose",
106,
(7, 67, 72, 9),
CS
| win32con.BS_AUTOCHECKBOX
| win32con.BS_LEFTTEXT
| win32con.WS_TABSTOP,
]
)
tmp.append(
[
BUTTON,
"OK",
win32con.IDOK,
(166, 53, 32, 12),
CS | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP,
]
)
tmp.append(
[
BUTTON,
"Cancel",
win32con.IDCANCEL,
(166, 67, 32, 12),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
dialog.Dialog.__init__(self, tmp)
self.AddDDX(101, "greppattern")
self.AddDDX(102, "dirpattern")
self.AddDDX(103, "filpattern")
self.AddDDX(104, "casesensitive")
self.AddDDX(105, "recursive")
self.AddDDX(106, "verbose")
self._obj_.data["greppattern"] = gp
self._obj_.data["dirpattern"] = dp
self._obj_.data["filpattern"] = fp
self._obj_.data["casesensitive"] = cs
self._obj_.data["recursive"] = r
self._obj_.data["verbose"] = v
self.HookCommand(self.OnMoreDirectories, 110)
self.HookCommand(self.OnMoreFiles, 111)
def OnMoreDirectories(self, cmd, code):
if code != 0:
return 1
self.getMore("Grep\\Directories", "dirpattern")
def OnMoreFiles(self, cmd, code):
if code != 0:
return 1
self.getMore("Grep\\File Types", "filpattern")
def getMore(self, section, key):
self.UpdateData(1)
# get the items out of the ini file
ini = win32ui.GetProfileFileName()
secitems = win32api.GetProfileSection(section, ini)
items = []
for secitem in secitems:
items.append(secitem.split("=")[1])
dlg = GrepParamsDialog(items)
if dlg.DoModal() == win32con.IDOK:
itemstr = ";".join(dlg.getItems())
self._obj_.data[key] = itemstr
# update the ini file with dlg.getNew()
i = 0
newitems = dlg.getNew()
if newitems:
items = items + newitems
for item in items:
win32api.WriteProfileVal(section, repr(i), item, ini)
i = i + 1
self.UpdateData(0)
def OnOK(self):
self.UpdateData(1)
for id, name in (
(101, "greppattern"),
(102, "dirpattern"),
(103, "filpattern"),
):
if not self[name]:
self.GetDlgItem(id).SetFocus()
win32api.MessageBeep()
win32ui.SetStatusText("Please enter a value")
return
self._obj_.OnOK()
class GrepParamsDialog(dialog.Dialog):
def __init__(self, items):
self.items = items
self.newitems = []
style = (
win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
CS = win32con.WS_CHILD | win32con.WS_VISIBLE
tmp = [
["Grep Parameters", (0, 0, 205, 100), style, None, (8, "MS Sans Serif")],
]
tmp.append(
[
LISTBOX,
"",
107,
(7, 7, 150, 72),
CS
| win32con.LBS_MULTIPLESEL
| win32con.LBS_STANDARD
| win32con.LBS_HASSTRINGS
| win32con.WS_TABSTOP
| win32con.LBS_NOTIFY,
]
)
tmp.append(
[
BUTTON,
"OK",
win32con.IDOK,
(167, 7, 32, 12),
CS | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP,
]
)
tmp.append(
[
BUTTON,
"Cancel",
win32con.IDCANCEL,
(167, 23, 32, 12),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
tmp.append([STATIC, "New:", -1, (2, 83, 15, 12), CS])
tmp.append(
[
EDIT,
"",
108,
(18, 83, 139, 12),
CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,
]
)
tmp.append(
[
BUTTON,
"Add",
109,
(167, 83, 32, 12),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
dialog.Dialog.__init__(self, tmp)
self.HookCommand(self.OnAddItem, 109)
self.HookCommand(self.OnListDoubleClick, 107)
def OnInitDialog(self):
lb = self.GetDlgItem(107)
for item in self.items:
lb.AddString(item)
return self._obj_.OnInitDialog()
def OnAddItem(self, cmd, code):
if code != 0:
return 1
eb = self.GetDlgItem(108)
item = eb.GetLine(0)
self.newitems.append(item)
lb = self.GetDlgItem(107)
i = lb.AddString(item)
lb.SetSel(i, 1)
return 1
def OnListDoubleClick(self, cmd, code):
if code == win32con.LBN_DBLCLK:
self.OnOK()
return 1
def OnOK(self):
lb = self.GetDlgItem(107)
self.selections = lb.GetSelTextItems()
self._obj_.OnOK()
def getItems(self):
return self.selections
def getNew(self):
return self.newitems
try:
win32ui.GetApp().RemoveDocTemplate(greptemplate)
except NameError:
pass
greptemplate = GrepTemplate()