# bicycle repair man idle extension import bike from bike.transformer.undo import UndoStackEmptyException import bike.parsing.load import os from Tkinter import * import tkFileDialog import tkMessageBox import tkSimpleDialog import sys import string try: from idlelib.PathBrowser import * from idlelib.WindowList import ListedToplevel from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas from idlelib import EditorWindow from idlelib.PyShell import PyShell from idlelib.OutputWindow import OutputWindow try: from idlelib.configHandler import idleConf except ImportError: pass except ImportError: from PathBrowser import* from WindowList import ListedToplevel from TreeWidget import TreeNode, TreeItem, ScrolledCanvas import EditorWindow from PyShell import PyShell from OutputWindow import OutputWindow try: from configHandler import idleConf except ImportError: pass brmctx = None shellwin = None loadingFiles = 0 matchwin = None class NotHighlightedException(Exception): pass class BicycleRepairMan_Idle: menudefs = [ ('bicycleRepairMan', [ ('----- Queries -----',''), ('_Find References','<>'), ('_Find Definition','<>'), None, ('--- Refactoring ---',''), ('_Rename', '<>'), ('_Extract Method', '<>'), None, ('_Undo', '<>'), ]) ] keydefs = { '<>':[], '<>':[], '<>':[], '<>':[], '<>':[], } try: TRACE = idleConf.GetOption('extensions','BicycleRepairMan_Idle', 'trace',default=1) except NameError: # hasnt imported idleconf - probably python 22 TRACE = 1 def __init__(self, editwin): self.editwin = editwin if self.TRACE == 1: self.progressLogger = ProgressLogger(self.editwin.flist) if not isinstance(editwin, PyShell): # sly'ly add the refactor menu to the window name, label = ("bicycleRepairMan", "_BicycleRepairMan") underline, label = EditorWindow.prepstr(label) mbar = editwin.menubar editwin.menudict[name] = menu = Menu(mbar, name = name) mbar.add_cascade(label = label, menu = menu, underline = underline) # Initialize Bicyclerepairman and import the code path = self.editwin.io.filename if path is not None: global brmctx if brmctx is None: self.initbrm() else: global shellwin shellwin = editwin def initbrm(self): global brmctx brmctx = bike.init() if self.TRACE == 1: brmctx.setProgressLogger(self.progressLogger) def brm_find_references_event(self,event): try: if not self.confirm_all_buffers_saved(): return if self.editwin.text.index("sel.first") == "": self.errorbox("Not highlighted", "Highlight the name of a Function, Class or Method and try again") return filename = os.path.normpath(self.editwin.io.filename) line, column = string.split(self.editwin.text.index("sel.first"),'.') numMatches = 0 global matchwin if matchwin is None: matchwin = BRMMatchesWindow(self.editwin.flist, self) matchwin.clear() for ref in brmctx.findReferencesByCoordinates(filename,int(line),int(column)): print >>matchwin, "File \""+ref.filename+"\", line "+str(ref.lineno)+", "+str(ref.confidence)+"% confidence" numMatches +=1 print >>matchwin, numMatches," matches" print >>matchwin, "(Hint: right-click to open locations.)" except: self._handleUnexpectedException() def brm_find_definition_event(self,event): try: if not self.confirm_all_buffers_saved(): return filename = os.path.normpath(self.editwin.io.filename) if self.editwin.text.index("sel.first") != "": line, column = string.split(self.editwin.text.index("sel.first"),'.') else: line, column = string.split(self.editwin.text.index("insert"), '.') defns = brmctx.findDefinitionByCoordinates(filename,int(line), int(column)) try: firstref = defns.next() editwin = self.editwin.flist.open(firstref.filename) editwin.gotoline(firstref.lineno) except StopIteration: self.errorbox("Couldn't Find definition","Couldn't Find definition") pass else: numRefs = 1 global matchwin if matchwin is None: matchwin = BRMMatchesWindow(self.editwin.flist, self) for ref in defns: if numRefs == 1: print >>matchwin, firstref.filename+":"+str(firstref.lineno)+": "+str(firstref.confidence)+"% confidence" numRefs += 1 print >>matchwin,ref.filename+":"+str(ref.lineno)+": "+str(ref.confidence)+"% confidence" if matchwin is not None: print >>matchwin, "(Hint: right-click to open locations.)" except: self._handleUnexpectedException() def brm_rename_event(self, event): try: self.renameItemByCoordinates() except: self._handleUnexpectedException() def brm_extract_method_event(self, event): try: if not self.confirm_all_buffers_saved(): return try: filename, newname, beginline, begincolumn, endline, endcolumn = self._getExtractionInformation("Method") except NotHighlightedException: return brmctx.extractMethod(filename, int(beginline), int(begincolumn), int(endline), int(endcolumn), newname) savedfiles = brmctx.save() self.refreshWindows(savedfiles, beginline) except: self._handleUnexpectedException() def brm_undo_event(self, event): try: line, column = string.split(self.editwin.text.index("insert"), '.') brmctx.undo() savedfiles = brmctx.save() self.refreshWindows(savedfiles, line) except UndoStackEmptyException: self.errorbox("Undo Stack Empty", "Undo Stack is empty") except: self._handleUnexpectedException() def _handleUnexpectedException(self): import traceback traceback.print_exc() self.errorbox("Caught Exception", "Caught Exception "+str(sys.exc_info()[0])) def _getExtractionInformation(self, extracttype): if self.editwin.text.index("sel.first") == "": self.errorbox("Code not highlighted", "Highlight the region of code you want to extract and try again") raise NotHighlightedException() filename = os.path.normpath(self.editwin.io.filename) newname = tkSimpleDialog.askstring("Extract Method ", "New "+extracttype+" Name:", parent = self.editwin.text) beginline, begincolumn = string.split(self.editwin.text.index("sel.first"), '.') endline, endcolumn = string.split(self.editwin.text.index("sel.last"), '.') return filename, newname, beginline, begincolumn, endline, endcolumn def renameMethodPromptCallback(self, filename, line, colbegin, colend): editwin = self.editwin.flist.open(filename) originaltop = self.editwin.getwindowlines()[0] # select the method call and position the window editwin.text.tag_remove("sel", "1.0", "end") editwin.text.tag_add("sel", str(line)+"."+str(colbegin), str(line)+"."+str(colend)) line, column = string.split(editwin.text.index("sel.first"), '.') editwin.text.yview(str(int(line)-2)+".0") d = NoFocusDialog("Rename?", "Cannot deduce the type of highlighted object reference.\nRename this declaration?", parent = editwin.text) # put the window back where it was self.editwin.text.yview(float(originaltop)) return d.answer def renameItemByCoordinates(self): if not self.confirm_all_buffers_saved(): return if self.editwin.text.index("sel.first") == "": self.errorbox("Name not highlighted", "Double click the name of the declaration you want to rename (to highlight it) and try again") return brmctx.setRenameMethodPromptCallback(self.renameMethodPromptCallback) line, column = string.split(self.editwin.text.index("sel.first"), '.') filename = os.path.normpath(self.editwin.io.filename) newname = tkSimpleDialog.askstring("Rename", "Rename to:", parent = self.editwin.text) if newname is None: # cancel clicked return brmctx.renameByCoordinates(filename, int(line), int(column), newname) savedfiles = brmctx.save() self.refreshWindows(savedfiles, line) def refreshWindows(self, savedfiles, line): # refresh editor windows oldtop = self.editwin.getwindowlines()[0] global loadingFiles loadingFiles = 1 for sf in savedfiles: normsf = os.path.normcase(sf) if normsf in self.editwin.flist.dict: editwin = self.editwin.flist.dict[normsf] editwin.io.loadfile(sf) loadingFiles = 0 self.editwin.text.mark_set("insert", float(line)) self.editwin.text.yview(float(oldtop)) def confirm_all_buffers_saved(self): filelist = self.editwin.flist.dict.keys() for f in filelist: #editwin = self.editwin.flist.open(f) editwin = self.editwin.flist.dict[f] if self.confirm_buffer_is_saved(editwin) == 0: return 0 return 1 def confirm_buffer_is_saved(self, editwin): if not editwin.get_saved(): name = (editwin.short_title()or editwin.long_title()or "Untitled") reply = tkMessageBox.askokcancel("Bicycle Repair Man", "The buffer for %s is not saved.\n\n"%name+ "Save it and continue?", master = self.editwin.text) self.editwin.text.focus_set() if reply: editwin.io.save(None) else: return 0 return 1 def errorbox(self, title, message): tkMessageBox.showerror(title, message, master = self.editwin.text) self.editwin.text.focus_set() class BRMTraceWindow(OutputWindow): def short_title(self): return "BicycleRepairMan Trace" class ProgressLogger: def __init__(self,flist): self.flist = flist def write(self,txt): if not hasattr(self,"io"): self.io = BRMTraceWindow(self.flist) try: self.io.write(txt) self.io.flush() except IOError: pass class NoFocusDialog(tkSimpleDialog._QueryDialog): def __init__(self, title, prompt, initialvalue = None, minvalue = None, maxvalue = None, parent = None): self.answer = 0 if not parent: import Tkinter parent = Tkinter._default_root self.prompt = prompt self.minvalue = minvalue self.maxvalue = maxvalue self.initialvalue = initialvalue Toplevel.__init__(self, parent) if title: self.title(title) self.parent = parent self.result = None body = Frame(self) self.initial_focus = self.body(body) body.pack(padx = 5, pady = 5) self.buttonbox() self.grab_set() self.protocol("WM_DELETE_WINDOW", self.cancel) if self.parent is not None: self.geometry("+%d+%d"%(parent.winfo_rootx()+50, parent.winfo_rooty()+50)) self.wait_window(self) def getresult(self): self.answer = 1 def body(self, master): w = Label(master, text = self.prompt, justify = LEFT) w.grid(row = 0, padx = 5, sticky = W) def buttonbox(self): box = Frame(self) w = Button(box, text = "Yes", width = 10, command = self.ok, default = ACTIVE) w.pack(side = LEFT, padx = 5, pady = 5) w = Button(box, text = "No", width = 10, command = self.cancel) w.pack(side = LEFT, padx = 5, pady = 5) self.bind("", self.ok) self.bind("", self.cancel) box.pack() class BRMMatchesWindow(OutputWindow): def __init__(self,flist,masterwin): OutputWindow.__init__(self,flist) self.masterwin = masterwin def close(self): global matchwin matchwin = None OutputWindow.close(self) def short_title(self): return "BicycleRepairMan Matches" def clear(self): self.text.delete("1.0","end-1c")