芝麻web文件管理V1.00
编辑当前文件:/home/conskgoa/doughi.co.uk/idlelib.tar
PathBrowser.py 0000644 00000005476 15217643351 0007401 0 ustar 00 import os import sys import importlib.machinery from idlelib.TreeWidget import TreeItem from idlelib.ClassBrowser import ClassBrowser, ModuleBrowserTreeItem class PathBrowser(ClassBrowser): def __init__(self, flist): self.init(flist) def settitle(self): self.top.wm_title("Path Browser") self.top.wm_iconname("Path Browser") def rootnode(self): return PathBrowserTreeItem() class PathBrowserTreeItem(TreeItem): def GetText(self): return "sys.path" def GetSubList(self): sublist = [] for dir in sys.path: item = DirBrowserTreeItem(dir) sublist.append(item) return sublist class DirBrowserTreeItem(TreeItem): def __init__(self, dir, packages=[]): self.dir = dir self.packages = packages def GetText(self): if not self.packages: return self.dir else: return self.packages[-1] + ": package" def GetSubList(self): try: names = os.listdir(self.dir or os.curdir) except os.error: return [] packages = [] for name in names: file = os.path.join(self.dir, name) if self.ispackagedir(file): nn = os.path.normcase(name) packages.append((nn, name, file)) packages.sort() sublist = [] for nn, name, file in packages: item = DirBrowserTreeItem(file, self.packages + [name]) sublist.append(item) for nn, name in self.listmodules(names): item = ModuleBrowserTreeItem(os.path.join(self.dir, name)) sublist.append(item) return sublist def ispackagedir(self, file): if not os.path.isdir(file): return 0 init = os.path.join(file, "__init__.py") return os.path.exists(init) def listmodules(self, allnames): modules = {} suffixes = importlib.machinery.EXTENSION_SUFFIXES[:] suffixes += importlib.machinery.SOURCE_SUFFIXES[:] suffixes += importlib.machinery.BYTECODE_SUFFIXES[:] sorted = [] for suff in suffixes: i = -len(suff) for name in allnames[:]: normed_name = os.path.normcase(name) if normed_name[i:] == suff: mod_name = name[:i] if mod_name not in modules: modules[mod_name] = None sorted.append((normed_name, name)) allnames.remove(name) sorted.sort() return sorted def main(): from idlelib import PyShell PathBrowser(PyShell.flist) if sys.stdin is sys.__stdin__: mainloop() if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_pathbrowser', verbosity=2, exit=False) MultiStatusBar.py 0000644 00000001417 15217643351 0010053 0 ustar 00 from tkinter import * class MultiStatusBar(Frame): def __init__(self, master=None, **kw): if master is None: master = Tk() Frame.__init__(self, master, **kw) self.labels = {} def set_label(self, name, text='', side=LEFT): if name not in self.labels: label = Label(self, bd=1, relief=SUNKEN, anchor=W) label.pack(side=side) self.labels[name] = label else: label = self.labels[name] label.config(text=text) def _test(): b = Frame() c = Text(b) c.pack(side=TOP) a = MultiStatusBar(b) a.set_label("one", "hello") a.set_label("two", "world") a.pack(side=BOTTOM, fill=X) b.pack() b.mainloop() if __name__ == '__main__': _test() AutoExpand.py 0000644 00000004663 15217643351 0007206 0 ustar 00 import string import re ###$ event <
> ###$ win
###$ unix
class AutoExpand: menudefs = [ ('edit', [ ('E_xpand Word', '<
>'), ]), ] wordchars = string.ascii_letters + string.digits + "_" def __init__(self, editwin): self.text = editwin.text self.state = None def expand_word_event(self, event): curinsert = self.text.index("insert") curline = self.text.get("insert linestart", "insert lineend") if not self.state: words = self.getwords() index = 0 else: words, index, insert, line = self.state if insert != curinsert or line != curline: words = self.getwords() index = 0 if not words: self.text.bell() return "break" word = self.getprevword() self.text.delete("insert - %d chars" % len(word), "insert") newword = words[index] index = (index + 1) % len(words) if index == 0: self.text.bell() # Warn we cycled around self.text.insert("insert", newword) curinsert = self.text.index("insert") curline = self.text.get("insert linestart", "insert lineend") self.state = words, index, curinsert, curline return "break" def getwords(self): word = self.getprevword() if not word: return [] before = self.text.get("1.0", "insert wordstart") wbefore = re.findall(r"\b" + word + r"\w+\b", before) del before after = self.text.get("insert wordend", "end") wafter = re.findall(r"\b" + word + r"\w+\b", after) del after if not wbefore and not wafter: return [] words = [] dict = {} # search backwards through words before wbefore.reverse() for w in wbefore: if dict.get(w): continue words.append(w) dict[w] = w # search onwards through words after for w in wafter: if dict.get(w): continue words.append(w) dict[w] = w words.append(word) return words def getprevword(self): line = self.text.get("insert linestart", "insert") i = len(line) while i > 0 and line[i-1] in self.wordchars: i = i-1 return line[i:] CodeContext.py 0000644 00000020241 15217643351 0007343 0 ustar 00 """CodeContext - Extension to display the block context above the edit window Once code has scrolled off the top of a window, it can be difficult to determine which block you are in. This extension implements a pane at the top of each IDLE edit window which provides block structure hints. These hints are the lines which contain the block opening keywords, e.g. 'if', for the enclosing block. The number of hint lines is determined by the numlines variable in the CodeContext section of config-extensions.def. Lines which do not open blocks are not shown in the context hints pane. """ import tkinter from tkinter.constants import TOP, LEFT, X, W, SUNKEN import re from sys import maxsize as INFINITY from idlelib.configHandler import idleConf BLOCKOPENERS = set(["class", "def", "elif", "else", "except", "finally", "for", "if", "try", "while", "with"]) UPDATEINTERVAL = 100 # millisec FONTUPDATEINTERVAL = 1000 # millisec getspacesfirstword =\ lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups() class CodeContext: menudefs = [('options', [('!Code Conte_xt', '<
>')])] context_depth = idleConf.GetOption("extensions", "CodeContext", "numlines", type="int", default=3) bgcolor = idleConf.GetOption("extensions", "CodeContext", "bgcolor", type="str", default="LightGray") fgcolor = idleConf.GetOption("extensions", "CodeContext", "fgcolor", type="str", default="Black") def __init__(self, editwin): self.editwin = editwin self.text = editwin.text self.textfont = self.text["font"] self.label = None # self.info is a list of (line number, indent level, line text, block # keyword) tuples providing the block structure associated with # self.topvisible (the linenumber of the line displayed at the top of # the edit window). self.info[0] is initialized as a 'dummy' line which # starts the toplevel 'block' of the module. self.info = [(0, -1, "", False)] self.topvisible = 1 visible = idleConf.GetOption("extensions", "CodeContext", "visible", type="bool", default=False) if visible: self.toggle_code_context_event() self.editwin.setvar('<
>', True) # Start two update cycles, one for context lines, one for font changes. self.text.after(UPDATEINTERVAL, self.timer_event) self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) def toggle_code_context_event(self, event=None): if not self.label: # Calculate the border width and horizontal padding required to # align the context with the text in the main Text widget. # # All values are passed through int(str(
)), since some # values may be pixel objects, which can't simply be added to ints. widgets = self.editwin.text, self.editwin.text_frame # Calculate the required vertical padding padx = 0 for widget in widgets: padx += int(str( widget.pack_info()['padx'] )) padx += int(str( widget.cget('padx') )) # Calculate the required border width border = 0 for widget in widgets: border += int(str( widget.cget('border') )) self.label = tkinter.Label(self.editwin.top, text="\n" * (self.context_depth - 1), anchor=W, justify=LEFT, font=self.textfont, bg=self.bgcolor, fg=self.fgcolor, width=1, #don't request more than we get padx=padx, border=border, relief=SUNKEN) # Pack the label widget before and above the text_frame widget, # thus ensuring that it will appear directly above text_frame self.label.pack(side=TOP, fill=X, expand=False, before=self.editwin.text_frame) else: self.label.destroy() self.label = None idleConf.SetOption("extensions", "CodeContext", "visible", str(self.label is not None)) idleConf.SaveUserCfgFiles() def get_line_info(self, linenum): """Get the line indent value, text, and any block start keyword If the line does not start a block, the keyword value is False. The indentation of empty lines (or comment lines) is INFINITY. """ text = self.text.get("%d.0" % linenum, "%d.end" % linenum) spaces, firstword = getspacesfirstword(text) opener = firstword in BLOCKOPENERS and firstword if len(text) == len(spaces) or text[len(spaces)] == '#': indent = INFINITY else: indent = len(spaces) return indent, text, opener def get_context(self, new_topvisible, stopline=1, stopindent=0): """Get context lines, starting at new_topvisible and working backwards. Stop when stopline or stopindent is reached. Return a tuple of context data and the indent level at the top of the region inspected. """ assert stopline > 0 lines = [] # The indentation level we are currently in: lastindent = INFINITY # For a line to be interesting, it must begin with a block opening # keyword, and have less indentation than lastindent. for linenum in range(new_topvisible, stopline-1, -1): indent, text, opener = self.get_line_info(linenum) if indent < lastindent: lastindent = indent if opener in ("else", "elif"): # We also show the if statement lastindent += 1 if opener and linenum < new_topvisible and indent >= stopindent: lines.append((linenum, indent, text, opener)) if lastindent <= stopindent: break lines.reverse() return lines, lastindent def update_code_context(self): """Update context information and lines visible in the context pane. """ new_topvisible = int(self.text.index("@0,0").split('.')[0]) if self.topvisible == new_topvisible: # haven't scrolled return if self.topvisible < new_topvisible: # scroll down lines, lastindent = self.get_context(new_topvisible, self.topvisible) # retain only context info applicable to the region # between topvisible and new_topvisible: while self.info[-1][1] >= lastindent: del self.info[-1] elif self.topvisible > new_topvisible: # scroll up stopindent = self.info[-1][1] + 1 # retain only context info associated # with lines above new_topvisible: while self.info[-1][0] >= new_topvisible: stopindent = self.info[-1][1] del self.info[-1] lines, lastindent = self.get_context(new_topvisible, self.info[-1][0]+1, stopindent) self.info.extend(lines) self.topvisible = new_topvisible # empty lines in context pane: context_strings = [""] * max(0, self.context_depth - len(self.info)) # followed by the context hint lines: context_strings += [x[2] for x in self.info[-self.context_depth:]] self.label["text"] = '\n'.join(context_strings) def timer_event(self): if self.label: self.update_code_context() self.text.after(UPDATEINTERVAL, self.timer_event) def font_timer_event(self): newtextfont = self.text["font"] if self.label and newtextfont != self.textfont: self.textfont = newtextfont self.label["font"] = self.textfont self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) GrepDialog.py 0000644 00000010017 15217643351 0007141 0 ustar 00 import os import fnmatch import sys from tkinter import * from idlelib import SearchEngine from idlelib.SearchDialogBase import SearchDialogBase def grep(text, io=None, flist=None): root = text._root() engine = SearchEngine.get(root) if not hasattr(engine, "_grepdialog"): engine._grepdialog = GrepDialog(root, engine, flist) dialog = engine._grepdialog searchphrase = text.get("sel.first", "sel.last") dialog.open(text, searchphrase, io) class GrepDialog(SearchDialogBase): title = "Find in Files Dialog" icon = "Grep" needwrapbutton = 0 def __init__(self, root, engine, flist): SearchDialogBase.__init__(self, root, engine) self.flist = flist self.globvar = StringVar(root) self.recvar = BooleanVar(root) def open(self, text, searchphrase, io=None): SearchDialogBase.open(self, text, searchphrase) if io: path = io.filename or "" else: path = "" dir, base = os.path.split(path) head, tail = os.path.splitext(base) if not tail: tail = ".py" self.globvar.set(os.path.join(dir, "*" + tail)) def create_entries(self): SearchDialogBase.create_entries(self) self.globent = self.make_entry("In files:", self.globvar) def create_other_buttons(self): f = self.make_frame() btn = Checkbutton(f, anchor="w", variable=self.recvar, text="Recurse down subdirectories") btn.pack(side="top", fill="both") btn.select() def create_command_buttons(self): SearchDialogBase.create_command_buttons(self) self.make_button("Search Files", self.default_command, 1) def default_command(self, event=None): prog = self.engine.getprog() if not prog: return path = self.globvar.get() if not path: self.top.bell() return from idlelib.OutputWindow import OutputWindow save = sys.stdout try: sys.stdout = OutputWindow(self.flist) self.grep_it(prog, path) finally: sys.stdout = save def grep_it(self, prog, path): dir, base = os.path.split(path) list = self.findfiles(dir, base, self.recvar.get()) list.sort() self.close() pat = self.engine.getpat() print("Searching %r in %s ..." % (pat, path)) hits = 0 for fn in list: try: with open(fn, errors='replace') as f: for lineno, line in enumerate(f, 1): if line[-1:] == '\n': line = line[:-1] if prog.search(line): sys.stdout.write("%s: %s: %s\n" % (fn, lineno, line)) hits += 1 except OSError as msg: print(msg) print(("Hits found: %s\n" "(Hint: right-click to open locations.)" % hits) if hits else "No hits.") def findfiles(self, dir, base, rec): try: names = os.listdir(dir or os.curdir) except OSError as msg: print(msg) return [] list = [] subdirs = [] for name in names: fn = os.path.join(dir, name) if os.path.isdir(fn): subdirs.append(fn) else: if fnmatch.fnmatch(name, base): list.append(fn) if rec: for subdir in subdirs: list.extend(self.findfiles(subdir, base, rec)) return list def close(self, event=None): if self.top: self.top.grab_release() self.top.withdraw() if __name__ == "__main__": # A human test is a bit tricky since EditorWindow() imports this module. # Hence Idle must be restarted after editing this file for a live test. import unittest unittest.main('idlelib.idle_test.test_grep', verbosity=2, exit=False) configSectionNameDialog.py 0000644 00000010332 15217643351 0011637 0 ustar 00 """ Dialog that allows user to specify a new config file section name. Used to get new highlight theme and keybinding set names. The 'return value' for the dialog, used two placed in configDialog.py, is the .result attribute set in the Ok and Cancel methods. """ from tkinter import * import tkinter.messagebox as tkMessageBox class GetCfgSectionNameDialog(Toplevel): def __init__(self, parent, title, message, used_names): """ message - string, informational message to display used_names - string collection, names already in use for validity check """ Toplevel.__init__(self, parent) self.configure(borderwidth=5) self.resizable(height=FALSE, width=FALSE) self.title(title) self.transient(parent) self.grab_set() self.protocol("WM_DELETE_WINDOW", self.Cancel) self.parent = parent self.message = message self.used_names = used_names self.create_widgets() self.withdraw() #hide while setting geometry self.update_idletasks() #needs to be done here so that the winfo_reqwidth is valid self.messageInfo.config(width=self.frameMain.winfo_reqwidth()) self.geometry( "+%d+%d" % ( parent.winfo_rootx() + (parent.winfo_width()/2 - self.winfo_reqwidth()/2), parent.winfo_rooty() + (parent.winfo_height()/2 - self.winfo_reqheight()/2) ) ) #centre dialog over parent self.deiconify() #geometry set, unhide self.wait_window() def create_widgets(self): self.name = StringVar(self.parent) self.fontSize = StringVar(self.parent) self.frameMain = Frame(self, borderwidth=2, relief=SUNKEN) self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH) self.messageInfo = Message(self.frameMain, anchor=W, justify=LEFT, padx=5, pady=5, text=self.message) #,aspect=200) entryName = Entry(self.frameMain, textvariable=self.name, width=30) entryName.focus_set() self.messageInfo.pack(padx=5, pady=5) #, expand=TRUE, fill=BOTH) entryName.pack(padx=5, pady=5) frameButtons = Frame(self, pady=2) frameButtons.pack(side=BOTTOM) self.buttonOk = Button(frameButtons, text='Ok', width=8, command=self.Ok) self.buttonOk.pack(side=LEFT, padx=5) self.buttonCancel = Button(frameButtons, text='Cancel', width=8, command=self.Cancel) self.buttonCancel.pack(side=RIGHT, padx=5) def name_ok(self): ''' After stripping entered name, check that it is a sensible ConfigParser file section name. Return it if it is, '' if not. ''' name = self.name.get().strip() if not name: #no name specified tkMessageBox.showerror(title='Name Error', message='No name specified.', parent=self) elif len(name)>30: #name too long tkMessageBox.showerror(title='Name Error', message='Name too long. It should be no more than '+ '30 characters.', parent=self) name = '' elif name in self.used_names: tkMessageBox.showerror(title='Name Error', message='This name is already in use.', parent=self) name = '' return name def Ok(self, event=None): name = self.name_ok() if name: self.result = name self.destroy() def Cancel(self, event=None): self.result = '' self.destroy() if __name__ == '__main__': import unittest unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False) # also human test the dialog root = Tk() def run(): dlg=GetCfgSectionNameDialog(root,'Get Name', "After the text entered with [Ok] is stripped,
, " "'abc', or more that 30 chars are errors. " "Close with a valid entry (printed), [Cancel], or [X]", {'abc'}) print(dlg.result) Message(root, text='').pack() # will be needed for oher dialog tests Button(root, text='Click to begin dialog test', command=run).pack() root.mainloop() RemoteObjectBrowser.py 0000644 00000001704 15217643351 0011055 0 ustar 00 from idlelib import rpc def remote_object_tree_item(item): wrapper = WrappedObjectTreeItem(item) oid = id(wrapper) rpc.objecttable[oid] = wrapper return oid class WrappedObjectTreeItem: # Lives in PYTHON subprocess def __init__(self, item): self.__item = item def __getattr__(self, name): value = getattr(self.__item, name) return value def _GetSubList(self): sub_list = self.__item._GetSubList() return list(map(remote_object_tree_item, sub_list)) class StubObjectTreeItem: # Lives in IDLE process def __init__(self, sockio, oid): self.sockio = sockio self.oid = oid def __getattr__(self, name): value = rpc.MethodProxy(self.sockio, self.oid, name) return value def _GetSubList(self): sub_list = self.sockio.remotecall(self.oid, "_GetSubList", (), {}) return [StubObjectTreeItem(self.sockio, oid) for oid in sub_list] CREDITS.txt 0000644 00000003511 15217643351 0006411 0 ustar 00 Guido van Rossum, as well as being the creator of the Python language, is the original creator of IDLE. Other contributors prior to Version 0.8 include Mark Hammond, Jeremy Hylton, Tim Peters, and Moshe Zadka. IDLE's recent development was carried out in the SF IDLEfork project. The objective was to develop a version of IDLE which had an execution environment which could be initialized prior to each run of user code. The IDLEfork project was initiated by David Scherer, with some help from Peter Schneider-Kamp and Nicholas Riley. David wrote the first version of the RPC code and designed a fast turn-around environment for VPython. Guido developed the RPC code and Remote Debugger currently integrated in IDLE. Bruce Sherwood contributed considerable time testing and suggesting improvements. Besides David and Guido, the main developers who were active on IDLEfork are Stephen M. Gava, who implemented the configuration GUI, the new configuration system, and the About dialog, and Kurt B. Kaiser, who completed the integration of the RPC and remote debugger, implemented the threaded subprocess, and made a number of usability enhancements. Other contributors include Raymond Hettinger, Tony Lownds (Mac integration), Neal Norwitz (code check and clean-up), Ronald Oussoren (Mac integration), Noam Raphael (Code Context, Call Tips, many other patches), and Chui Tey (RPC integration, debugger integration and persistent breakpoints). Scott David Daniels, Tal Einat, Hernan Foffani, Christos Georgiou, Jim Jewett, Martin v. Lwis, Jason Orendorff, Guilherme Polo, Josh Robb, Nigel Rowe, Bruce Sherwood, Jeff Shute, and Weeble have submitted useful patches. Thanks, guys! For additional details refer to NEWS.txt and Changelog. Please contact the IDLE maintainer (kbk@shore.net) to have yourself included here if you are one of those we missed! FileList.py 0000644 00000007346 15217643351 0006652 0 ustar 00 import os from tkinter import * import tkinter.messagebox as tkMessageBox class FileList: # N.B. this import overridden in PyShellFileList. from idlelib.EditorWindow import EditorWindow def __init__(self, root): self.root = root self.dict = {} self.inversedict = {} self.vars = {} # For EditorWindow.getrawvar (shared Tcl variables) def open(self, filename, action=None): assert filename filename = self.canonize(filename) if os.path.isdir(filename): # This can happen when bad filename is passed on command line: tkMessageBox.showerror( "File Error", "%r is a directory." % (filename,), master=self.root) return None key = os.path.normcase(filename) if key in self.dict: edit = self.dict[key] edit.top.wakeup() return edit if action: # Don't create window, perform 'action', e.g. open in same window return action(filename) else: edit = self.EditorWindow(self, filename, key) if edit.good_load: return edit else: edit._close() return None def gotofileline(self, filename, lineno=None): edit = self.open(filename) if edit is not None and lineno is not None: edit.gotoline(lineno) def new(self, filename=None): return self.EditorWindow(self, filename) def close_all_callback(self, *args, **kwds): for edit in list(self.inversedict): reply = edit.close() if reply == "cancel": break return "break" def unregister_maybe_terminate(self, edit): try: key = self.inversedict[edit] except KeyError: print("Don't know this EditorWindow object. (close)") return if key: del self.dict[key] del self.inversedict[edit] if not self.inversedict: self.root.quit() def filename_changed_edit(self, edit): edit.saved_change_hook() try: key = self.inversedict[edit] except KeyError: print("Don't know this EditorWindow object. (rename)") return filename = edit.io.filename if not filename: if key: del self.dict[key] self.inversedict[edit] = None return filename = self.canonize(filename) newkey = os.path.normcase(filename) if newkey == key: return if newkey in self.dict: conflict = self.dict[newkey] self.inversedict[conflict] = None tkMessageBox.showerror( "Name Conflict", "You now have multiple edit windows open for %r" % (filename,), master=self.root) self.dict[newkey] = edit self.inversedict[edit] = newkey if key: try: del self.dict[key] except KeyError: pass def canonize(self, filename): if not os.path.isabs(filename): try: pwd = os.getcwd() except os.error: pass else: filename = os.path.join(pwd, filename) return os.path.normpath(filename) def _test(): from idlelib.EditorWindow import fixwordbreaks import sys root = Tk() fixwordbreaks(root) root.withdraw() flist = FileList(root) if sys.argv[1:]: for filename in sys.argv[1:]: flist.open(filename) else: flist.new() if flist.inversedict: root.mainloop() if __name__ == '__main__': _test() AutoComplete.py 0000644 00000021572 15217643351 0007535 0 ustar 00 """AutoComplete.py - An IDLE extension for automatically completing names. This extension can complete either attribute names of file names. It can pop a window with all available names, for the user to select from. """ import os import sys import string from idlelib.configHandler import idleConf # This string includes all chars that may be in an identifier ID_CHARS = string.ascii_letters + string.digits + "_" # These constants represent the two different types of completions COMPLETE_ATTRIBUTES, COMPLETE_FILES = range(1, 2+1) from idlelib import AutoCompleteWindow from idlelib.HyperParser import HyperParser import __main__ SEPS = os.sep if os.altsep: # e.g. '/' on Windows... SEPS += os.altsep class AutoComplete: menudefs = [ ('edit', [ ("Show Completions", "<
>"), ]) ] popupwait = idleConf.GetOption("extensions", "AutoComplete", "popupwait", type="int", default=0) def __init__(self, editwin=None): self.editwin = editwin if editwin is None: # subprocess and test return self.text = editwin.text self.autocompletewindow = None # id of delayed call, and the index of the text insert when the delayed # call was issued. If _delayed_completion_id is None, there is no # delayed call. self._delayed_completion_id = None self._delayed_completion_index = None def _make_autocomplete_window(self): return AutoCompleteWindow.AutoCompleteWindow(self.text) def _remove_autocomplete_window(self, event=None): if self.autocompletewindow: self.autocompletewindow.hide_window() self.autocompletewindow = None def force_open_completions_event(self, event): """Happens when the user really wants to open a completion list, even if a function call is needed. """ self.open_completions(True, False, True) def try_open_completions_event(self, event): """Happens when it would be nice to open a completion list, but not really necessary, for example after an dot, so function calls won't be made. """ lastchar = self.text.get("insert-1c") if lastchar == ".": self._open_completions_later(False, False, False, COMPLETE_ATTRIBUTES) elif lastchar in SEPS: self._open_completions_later(False, False, False, COMPLETE_FILES) def autocomplete_event(self, event): """Happens when the user wants to complete his word, and if necessary, open a completion list after that (if there is more than one completion) """ if hasattr(event, "mc_state") and event.mc_state: # A modifier was pressed along with the tab, continue as usual. return if self.autocompletewindow and self.autocompletewindow.is_active(): self.autocompletewindow.complete() return "break" else: opened = self.open_completions(False, True, True) if opened: return "break" def _open_completions_later(self, *args): self._delayed_completion_index = self.text.index("insert") if self._delayed_completion_id is not None: self.text.after_cancel(self._delayed_completion_id) self._delayed_completion_id = \ self.text.after(self.popupwait, self._delayed_open_completions, *args) def _delayed_open_completions(self, *args): self._delayed_completion_id = None if self.text.index("insert") != self._delayed_completion_index: return self.open_completions(*args) def open_completions(self, evalfuncs, complete, userWantsWin, mode=None): """Find the completions and create the AutoCompleteWindow. Return True if successful (no syntax error or so found). if complete is True, then if there's nothing to complete and no start of completion, won't open completions and return False. If mode is given, will open a completion list only in this mode. """ # Cancel another delayed call, if it exists. if self._delayed_completion_id is not None: self.text.after_cancel(self._delayed_completion_id) self._delayed_completion_id = None hp = HyperParser(self.editwin, "insert") curline = self.text.get("insert linestart", "insert") i = j = len(curline) if hp.is_in_string() and (not mode or mode==COMPLETE_FILES): # Find the beginning of the string # fetch_completions will look at the file system to determine whether the # string value constitutes an actual file name # XXX could consider raw strings here and unescape the string value if it's # not raw. self._remove_autocomplete_window() mode = COMPLETE_FILES # Find last separator or string start while i and curline[i-1] not in "'\"" + SEPS: i -= 1 comp_start = curline[i:j] j = i # Find string start while i and curline[i-1] not in "'\"": i -= 1 comp_what = curline[i:j] elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES): self._remove_autocomplete_window() mode = COMPLETE_ATTRIBUTES while i and (curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127): i -= 1 comp_start = curline[i:j] if i and curline[i-1] == '.': hp.set_index("insert-%dc" % (len(curline)-(i-1))) comp_what = hp.get_expression() if not comp_what or \ (not evalfuncs and comp_what.find('(') != -1): return else: comp_what = "" else: return if complete and not comp_what and not comp_start: return comp_lists = self.fetch_completions(comp_what, mode) if not comp_lists[0]: return self.autocompletewindow = self._make_autocomplete_window() return not self.autocompletewindow.show_window( comp_lists, "insert-%dc" % len(comp_start), complete, mode, userWantsWin) def fetch_completions(self, what, mode): """Return a pair of lists of completions for something. The first list is a sublist of the second. Both are sorted. If there is a Python subprocess, get the comp. list there. Otherwise, either fetch_completions() is running in the subprocess itself or it was called in an IDLE EditorWindow before any script had been run. The subprocess environment is that of the most recently run script. If two unrelated modules are being edited some calltips in the current module may be inoperative if the module was not the last to run. """ try: rpcclt = self.editwin.flist.pyshell.interp.rpcclt except: rpcclt = None if rpcclt: return rpcclt.remotecall("exec", "get_the_completion_list", (what, mode), {}) else: if mode == COMPLETE_ATTRIBUTES: if what == "": namespace = __main__.__dict__.copy() namespace.update(__main__.__builtins__.__dict__) bigl = eval("dir()", namespace) bigl.sort() if "__all__" in bigl: smalll = sorted(eval("__all__", namespace)) else: smalll = [s for s in bigl if s[:1] != '_'] else: try: entity = self.get_entity(what) bigl = dir(entity) bigl.sort() if "__all__" in bigl: smalll = sorted(entity.__all__) else: smalll = [s for s in bigl if s[:1] != '_'] except: return [], [] elif mode == COMPLETE_FILES: if what == "": what = "." try: expandedpath = os.path.expanduser(what) bigl = os.listdir(expandedpath) bigl.sort() smalll = [s for s in bigl if s[:1] != '.'] except OSError: return [], [] if not smalll: smalll = bigl return smalll, bigl def get_entity(self, name): """Lookup name in a namespace spanning sys.modules and __main.dict__""" namespace = sys.modules.copy() namespace.update(__main__.__dict__) return eval(name, namespace) EditorWindow.py 0000644 00000201403 15217643351 0007543 0 ustar 00 import importlib import importlib.abc import os from platform import python_version import re import string import sys from tkinter import * import tkinter.simpledialog as tkSimpleDialog import tkinter.messagebox as tkMessageBox import traceback import webbrowser from idlelib.MultiCall import MultiCallCreator from idlelib import idlever from idlelib import WindowList from idlelib import SearchDialog from idlelib import GrepDialog from idlelib import ReplaceDialog from idlelib import PyParse from idlelib.configHandler import idleConf from idlelib import aboutDialog, textView, configDialog from idlelib import macosxSupport # The default tab setting for a Text widget, in average-width characters. TK_TABWIDTH_DEFAULT = 8 def _sphinx_version(): "Format sys.version_info to produce the Sphinx version string used to install the chm docs" major, minor, micro, level, serial = sys.version_info release = '%s%s' % (major, minor) release += '%s' % (micro,) if level == 'candidate': release += 'rc%s' % (serial,) elif level != 'final': release += '%s%s' % (level[0], serial) return release class HelpDialog(object): def __init__(self): self.parent = None # parent of help window self.dlg = None # the help window iteself def display(self, parent, near=None): """ Display the help dialog. parent - parent widget for the help window near - a Toplevel widget (e.g. EditorWindow or PyShell) to use as a reference for placing the help window """ if self.dlg is None: self.show_dialog(parent) if near: self.nearwindow(near) def show_dialog(self, parent): self.parent = parent fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt') self.dlg = dlg = textView.view_file(parent,'Help',fn, modal=False) dlg.bind('
', self.destroy, '+') def nearwindow(self, near): # Place the help dialog near the window specified by parent. # Note - this may not reposition the window in Metacity # if "/apps/metacity/general/disable_workarounds" is enabled dlg = self.dlg geom = (near.winfo_rootx() + 10, near.winfo_rooty() + 10) dlg.withdraw() dlg.geometry("=+%d+%d" % geom) dlg.deiconify() dlg.lift() def destroy(self, ev=None): self.dlg = None self.parent = None helpDialog = HelpDialog() # singleton instance class EditorWindow(object): from idlelib.Percolator import Percolator from idlelib.ColorDelegator import ColorDelegator from idlelib.UndoDelegator import UndoDelegator from idlelib.IOBinding import IOBinding, filesystemencoding, encoding from idlelib import Bindings from tkinter import Toplevel from idlelib.MultiStatusBar import MultiStatusBar help_url = None def __init__(self, flist=None, filename=None, key=None, root=None): if EditorWindow.help_url is None: dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html') if sys.platform.count('linux'): # look for html docs in a couple of standard places pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3] if os.path.isdir('/var/www/html/python/'): # "python2" rpm dochome = '/var/www/html/python/index.html' else: basepath = '/usr/share/doc/' # standard location dochome = os.path.join(basepath, pyver, 'Doc', 'index.html') elif sys.platform[:3] == 'win': chmfile = os.path.join(sys.base_prefix, 'Doc', 'Python%s.chm' % _sphinx_version()) if os.path.isfile(chmfile): dochome = chmfile elif macosxSupport.runningAsOSXApp(): # documentation is stored inside the python framework dochome = os.path.join(sys.base_prefix, 'Resources/English.lproj/Documentation/index.html') dochome = os.path.normpath(dochome) if os.path.isfile(dochome): EditorWindow.help_url = dochome if sys.platform == 'darwin': # Safari requires real file:-URLs EditorWindow.help_url = 'file://' + EditorWindow.help_url else: EditorWindow.help_url = "http://docs.python.org/%d.%d" % sys.version_info[:2] currentTheme=idleConf.CurrentTheme() self.flist = flist root = root or flist.root self.root = root try: sys.ps1 except AttributeError: sys.ps1 = '>>> ' self.menubar = Menu(root) self.top = top = WindowList.ListedToplevel(root, menu=self.menubar) if flist: self.tkinter_vars = flist.vars #self.top.instance_dict makes flist.inversedict available to #configDialog.py so it can access all EditorWindow instances self.top.instance_dict = flist.inversedict else: self.tkinter_vars = {} # keys: Tkinter event names # values: Tkinter variable instances self.top.instance_dict = {} self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(), 'recent-files.lst') self.text_frame = text_frame = Frame(top) self.vbar = vbar = Scrollbar(text_frame, name='vbar') self.width = idleConf.GetOption('main', 'EditorWindow', 'width', type='int') text_options = { 'name': 'text', 'padx': 5, 'wrap': 'none', 'width': self.width, 'height': idleConf.GetOption('main', 'EditorWindow', 'height', type='int')} if TkVersion >= 8.5: # Starting with tk 8.5 we have to set the new tabstyle option # to 'wordprocessor' to achieve the same display of tabs as in # older tk versions. text_options['tabstyle'] = 'wordprocessor' self.text = text = MultiCallCreator(Text)(text_frame, **text_options) self.top.focused_widget = self.text self.createmenubar() self.apply_bindings() self.top.protocol("WM_DELETE_WINDOW", self.close) self.top.bind("<
>", self.close_event) if macosxSupport.runningAsOSXApp(): # Command-W on editorwindows doesn't work without this. text.bind('<
>', self.close_event) # Some OS X systems have only one mouse button, # so use control-click for pulldown menus there. # (Note, AquaTk defines <2> as the right button if # present and the Tk Text widget already binds <2>.) text.bind("
",self.right_menu_event) else: # Elsewhere, use right-click for pulldown menus. text.bind("<3>",self.right_menu_event) text.bind("<
>", self.cut) text.bind("<
>", self.copy) text.bind("<
>", self.paste) text.bind("<
>", self.center_insert_event) text.bind("<
>", self.help_dialog) text.bind("<
>", self.python_docs) text.bind("<
>", self.about_dialog) text.bind("<
>", self.config_dialog) text.bind("<
>", self.open_module) text.bind("<
>", lambda event: "break") text.bind("<
>", self.select_all) text.bind("<
>", self.remove_selection) text.bind("<
>", self.find_event) text.bind("<
>", self.find_again_event) text.bind("<
>", self.find_in_files_event) text.bind("<
>", self.find_selection_event) text.bind("<
>", self.replace_event) text.bind("<
>", self.goto_line_event) text.bind("<
>",self.smart_backspace_event) text.bind("<
>",self.newline_and_indent_event) text.bind("<
>",self.smart_indent_event) text.bind("<
>",self.indent_region_event) text.bind("<
>",self.dedent_region_event) text.bind("<
>",self.comment_region_event) text.bind("<
>",self.uncomment_region_event) text.bind("<
>",self.tabify_region_event) text.bind("<
>",self.untabify_region_event) text.bind("<
>",self.toggle_tabs_event) text.bind("<
>",self.change_indentwidth_event) text.bind("
", self.move_at_edge_if_selection(0)) text.bind("
", self.move_at_edge_if_selection(1)) text.bind("<
>", self.del_word_left) text.bind("<
>", self.del_word_right) text.bind("<
>", self.home_callback) if flist: flist.inversedict[self] = key if key: flist.dict[key] = self text.bind("<
>", self.new_callback) text.bind("<
>", self.flist.close_all_callback) text.bind("<
>", self.open_class_browser) text.bind("<
>", self.open_path_browser) self.set_status_bar() vbar['command'] = text.yview vbar.pack(side=RIGHT, fill=Y) text['yscrollcommand'] = vbar.set fontWeight = 'normal' if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'): fontWeight='bold' text.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'), idleConf.GetOption('main', 'EditorWindow', 'font-size', type='int'), fontWeight)) text_frame.pack(side=LEFT, fill=BOTH, expand=1) text.pack(side=TOP, fill=BOTH, expand=1) text.focus_set() # usetabs true -> literal tab characters are used by indent and # dedent cmds, possibly mixed with spaces if # indentwidth is not a multiple of tabwidth, # which will cause Tabnanny to nag! # false -> tab characters are converted to spaces by indent # and dedent cmds, and ditto TAB keystrokes # Although use-spaces=0 can be configured manually in config-main.def, # configuration of tabs v. spaces is not supported in the configuration # dialog. IDLE promotes the preferred Python indentation: use spaces! usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces', type='bool') self.usetabs = not usespaces # tabwidth is the display width of a literal tab character. # CAUTION: telling Tk to use anything other than its default # tab setting causes it to use an entirely different tabbing algorithm, # treating tab stops as fixed distances from the left margin. # Nobody expects this, so for now tabwidth should never be changed. self.tabwidth = 8 # must remain 8 until Tk is fixed. # indentwidth is the number of screen characters per indent level. # The recommended Python indentation is four spaces. self.indentwidth = self.tabwidth self.set_notabs_indentwidth() # If context_use_ps1 is true, parsing searches back for a ps1 line; # else searches for a popular (if, def, ...) Python stmt. self.context_use_ps1 = False # When searching backwards for a reliable place to begin parsing, # first start num_context_lines[0] lines back, then # num_context_lines[1] lines back if that didn't work, and so on. # The last value should be huge (larger than the # of lines in a # conceivable file). # Making the initial values larger slows things down more often. self.num_context_lines = 50, 500, 5000000 self.per = per = self.Percolator(text) self.undo = undo = self.UndoDelegator() per.insertfilter(undo) text.undo_block_start = undo.undo_block_start text.undo_block_stop = undo.undo_block_stop undo.set_saved_change_hook(self.saved_change_hook) # IOBinding implements file I/O and printing functionality self.io = io = self.IOBinding(self) io.set_filename_change_hook(self.filename_change_hook) self.good_load = False self.set_indentation_params(False) self.color = None # initialized below in self.ResetColorizer if filename: if os.path.exists(filename) and not os.path.isdir(filename): if io.loadfile(filename): self.good_load = True is_py_src = self.ispythonsource(filename) self.set_indentation_params(is_py_src) else: io.set_filename(filename) self.good_load = True self.ResetColorizer() self.saved_change_hook() self.update_recent_files_list() self.load_extensions() menu = self.menudict.get('windows') if menu: end = menu.index("end") if end is None: end = -1 if end >= 0: menu.add_separator() end = end + 1 self.wmenu_end = end WindowList.register_callback(self.postwindowsmenu) # Some abstractions so IDLE extensions are cross-IDE self.askyesno = tkMessageBox.askyesno self.askinteger = tkSimpleDialog.askinteger self.showerror = tkMessageBox.showerror self._highlight_workaround() # Fix selection tags on Windows def _highlight_workaround(self): # On Windows, Tk removes painting of the selection # tags which is different behavior than on Linux and Mac. # See issue14146 for more information. if not sys.platform.startswith('win'): return text = self.text text.event_add("<
>", "
") text.event_add("<
>", "
") def highlight_fix(focus): sel_range = text.tag_ranges("sel") if sel_range: if focus == 'out': HILITE_CONFIG = idleConf.GetHighlight( idleConf.CurrentTheme(), 'hilite') text.tag_config("sel_fix", HILITE_CONFIG) text.tag_raise("sel_fix") text.tag_add("sel_fix", *sel_range) elif focus == 'in': text.tag_remove("sel_fix", "1.0", "end") text.bind("<
>", lambda ev: highlight_fix("out")) text.bind("<
>", lambda ev: highlight_fix("in")) def _filename_to_unicode(self, filename): """convert filename to unicode in order to display it in Tk""" if isinstance(filename, str) or not filename: return filename else: try: return filename.decode(self.filesystemencoding) except UnicodeDecodeError: # XXX try: return filename.decode(self.encoding) except UnicodeDecodeError: # byte-to-byte conversion return filename.decode('iso8859-1') def new_callback(self, event): dirname, basename = self.io.defaultfilename() self.flist.new(dirname) return "break" def home_callback(self, event): if (event.state & 4) != 0 and event.keysym == "Home": # state&4==Control. If
, use the Tk binding. return if self.text.index("iomark") and \ self.text.compare("iomark", "<=", "insert lineend") and \ self.text.compare("insert linestart", "<=", "iomark"): # In Shell on input line, go to just after prompt insertpt = int(self.text.index("iomark").split(".")[1]) else: line = self.text.get("insert linestart", "insert lineend") for insertpt in range(len(line)): if line[insertpt] not in (' ','\t'): break else: insertpt=len(line) lineat = int(self.text.index("insert").split('.')[1]) if insertpt == lineat: insertpt = 0 dest = "insert linestart+"+str(insertpt)+"c" if (event.state&1) == 0: # shift was not pressed self.text.tag_remove("sel", "1.0", "end") else: if not self.text.index("sel.first"): # there was no previous selection self.text.mark_set("my_anchor", "insert") else: if self.text.compare(self.text.index("sel.first"), "<", self.text.index("insert")): self.text.mark_set("my_anchor", "sel.first") # extend back else: self.text.mark_set("my_anchor", "sel.last") # extend forward first = self.text.index(dest) last = self.text.index("my_anchor") if self.text.compare(first,">",last): first,last = last,first self.text.tag_remove("sel", "1.0", "end") self.text.tag_add("sel", first, last) self.text.mark_set("insert", dest) self.text.see("insert") return "break" def set_status_bar(self): self.status_bar = self.MultiStatusBar(self.top) if macosxSupport.runningAsOSXApp(): # Insert some padding to avoid obscuring some of the statusbar # by the resize widget. self.status_bar.set_label('_padding1', ' ', side=RIGHT) self.status_bar.set_label('column', 'Col: ?', side=RIGHT) self.status_bar.set_label('line', 'Ln: ?', side=RIGHT) self.status_bar.pack(side=BOTTOM, fill=X) self.text.bind("<
>", self.set_line_and_column) self.text.event_add("<
>", "
", "
") self.text.after_idle(self.set_line_and_column) def set_line_and_column(self, event=None): line, column = self.text.index(INSERT).split('.') self.status_bar.set_label('column', 'Col: %s' % column) self.status_bar.set_label('line', 'Ln: %s' % line) menu_specs = [ ("file", "_File"), ("edit", "_Edit"), ("format", "F_ormat"), ("run", "_Run"), ("options", "_Options"), ("windows", "_Windows"), ("help", "_Help"), ] if macosxSupport.runningAsOSXApp(): menu_specs[-2] = ("windows", "_Window") def createmenubar(self): mbar = self.menubar self.menudict = menudict = {} for name, label in self.menu_specs: underline, label = prepstr(label) menudict[name] = menu = Menu(mbar, name=name) mbar.add_cascade(label=label, menu=menu, underline=underline) if macosxSupport.isCarbonAquaTk(self.root): # Insert the application menu menudict['application'] = menu = Menu(mbar, name='apple') mbar.add_cascade(label='IDLE', menu=menu) self.fill_menus() self.recent_files_menu = Menu(self.menubar) self.menudict['file'].insert_cascade(3, label='Recent Files', underline=0, menu=self.recent_files_menu) self.base_helpmenu_length = self.menudict['help'].index(END) self.reset_help_menu_entries() def postwindowsmenu(self): # Only called when Windows menu exists menu = self.menudict['windows'] end = menu.index("end") if end is None: end = -1 if end > self.wmenu_end: menu.delete(self.wmenu_end+1, end) WindowList.add_windows_to_menu(menu) rmenu = None def right_menu_event(self, event): self.text.mark_set("insert", "@%d,%d" % (event.x, event.y)) if not self.rmenu: self.make_rmenu() rmenu = self.rmenu self.event = event iswin = sys.platform[:3] == 'win' if iswin: self.text.config(cursor="arrow") for item in self.rmenu_specs: try: label, eventname, verify_state = item except ValueError: # see issue1207589 continue if verify_state is None: continue state = getattr(self, verify_state)() rmenu.entryconfigure(label, state=state) rmenu.tk_popup(event.x_root, event.y_root) if iswin: self.text.config(cursor="ibeam") rmenu_specs = [ # ("Label", "<
>", "statefuncname"), ... ("Close", "<
>", None), # Example ] def make_rmenu(self): rmenu = Menu(self.text, tearoff=0) for item in self.rmenu_specs: label, eventname = item[0], item[1] if label is not None: def command(text=self.text, eventname=eventname): text.event_generate(eventname) rmenu.add_command(label=label, command=command) else: rmenu.add_separator() self.rmenu = rmenu def rmenu_check_cut(self): return self.rmenu_check_copy() def rmenu_check_copy(self): try: indx = self.text.index('sel.first') except TclError: return 'disabled' else: return 'normal' if indx else 'disabled' def rmenu_check_paste(self): try: self.text.tk.call('tk::GetSelection', self.text, 'CLIPBOARD') except TclError: return 'disabled' else: return 'normal' def about_dialog(self, event=None): aboutDialog.AboutDialog(self.top,'About IDLE') def config_dialog(self, event=None): configDialog.ConfigDialog(self.top,'Settings') def help_dialog(self, event=None): if self.root: parent = self.root else: parent = self.top helpDialog.display(parent, near=self.top) def python_docs(self, event=None): if sys.platform[:3] == 'win': try: os.startfile(self.help_url) except WindowsError as why: tkMessageBox.showerror(title='Document Start Failure', message=str(why), parent=self.text) else: webbrowser.open(self.help_url) return "break" def cut(self,event): self.text.event_generate("<
>") return "break" def copy(self,event): if not self.text.tag_ranges("sel"): # There is no selection, so do nothing and maybe interrupt. return self.text.event_generate("<
>") return "break" def paste(self,event): self.text.event_generate("<
>") self.text.see("insert") return "break" def select_all(self, event=None): self.text.tag_add("sel", "1.0", "end-1c") self.text.mark_set("insert", "1.0") self.text.see("insert") return "break" def remove_selection(self, event=None): self.text.tag_remove("sel", "1.0", "end") self.text.see("insert") def move_at_edge_if_selection(self, edge_index): """Cursor move begins at start or end of selection When a left/right cursor key is pressed create and return to Tkinter a function which causes a cursor move from the associated edge of the selection. """ self_text_index = self.text.index self_text_mark_set = self.text.mark_set edges_table = ("sel.first+1c", "sel.last-1c") def move_at_edge(event): if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed try: self_text_index("sel.first") self_text_mark_set("insert", edges_table[edge_index]) except TclError: pass return move_at_edge def del_word_left(self, event): self.text.event_generate('
') return "break" def del_word_right(self, event): self.text.event_generate('
') return "break" def find_event(self, event): SearchDialog.find(self.text) return "break" def find_again_event(self, event): SearchDialog.find_again(self.text) return "break" def find_selection_event(self, event): SearchDialog.find_selection(self.text) return "break" def find_in_files_event(self, event): GrepDialog.grep(self.text, self.io, self.flist) return "break" def replace_event(self, event): ReplaceDialog.replace(self.text) return "break" def goto_line_event(self, event): text = self.text lineno = tkSimpleDialog.askinteger("Goto", "Go to line number:",parent=text) if lineno is None: return "break" if lineno <= 0: text.bell() return "break" text.mark_set("insert", "%d.0" % lineno) text.see("insert") def open_module(self, event=None): # XXX Shouldn't this be in IOBinding? try: name = self.text.get("sel.first", "sel.last") except TclError: name = "" else: name = name.strip() name = tkSimpleDialog.askstring("Module", "Enter the name of a Python module\n" "to search on sys.path and open:", parent=self.text, initialvalue=name) if name: name = name.strip() if not name: return # XXX Ought to insert current file's directory in front of path try: loader = importlib.find_loader(name) except (ValueError, ImportError) as msg: tkMessageBox.showerror("Import error", str(msg), parent=self.text) return if loader is None: tkMessageBox.showerror("Import error", "module not found", parent=self.text) return if not isinstance(loader, importlib.abc.SourceLoader): tkMessageBox.showerror("Import error", "not a source-based module", parent=self.text) return try: file_path = loader.get_filename(name) except AttributeError: tkMessageBox.showerror("Import error", "loader does not support get_filename", parent=self.text) return if self.flist: self.flist.open(file_path) else: self.io.loadfile(file_path) def open_class_browser(self, event=None): filename = self.io.filename if not filename: tkMessageBox.showerror( "No filename", "This buffer has no associated filename", master=self.text) self.text.focus_set() return None head, tail = os.path.split(filename) base, ext = os.path.splitext(tail) from idlelib import ClassBrowser ClassBrowser.ClassBrowser(self.flist, base, [head]) def open_path_browser(self, event=None): from idlelib import PathBrowser PathBrowser.PathBrowser(self.flist) def gotoline(self, lineno): if lineno is not None and lineno > 0: self.text.mark_set("insert", "%d.0" % lineno) self.text.tag_remove("sel", "1.0", "end") self.text.tag_add("sel", "insert", "insert +1l") self.center() def ispythonsource(self, filename): if not filename or os.path.isdir(filename): return True base, ext = os.path.splitext(os.path.basename(filename)) if os.path.normcase(ext) in (".py", ".pyw"): return True line = self.text.get('1.0', '1.0 lineend') return line.startswith('#!') and 'python' in line def close_hook(self): if self.flist: self.flist.unregister_maybe_terminate(self) self.flist = None def set_close_hook(self, close_hook): self.close_hook = close_hook def filename_change_hook(self): if self.flist: self.flist.filename_changed_edit(self) self.saved_change_hook() self.top.update_windowlist_registry(self) self.ResetColorizer() def _addcolorizer(self): if self.color: return if self.ispythonsource(self.io.filename): self.color = self.ColorDelegator() # can add more colorizers here... if self.color: self.per.removefilter(self.undo) self.per.insertfilter(self.color) self.per.insertfilter(self.undo) def _rmcolorizer(self): if not self.color: return self.color.removecolors() self.per.removefilter(self.color) self.color = None def ResetColorizer(self): "Update the colour theme" # Called from self.filename_change_hook and from configDialog.py self._rmcolorizer() self._addcolorizer() theme = idleConf.GetOption('main','Theme','name') normal_colors = idleConf.GetHighlight(theme, 'normal') cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg') select_colors = idleConf.GetHighlight(theme, 'hilite') self.text.config( foreground=normal_colors['foreground'], background=normal_colors['background'], insertbackground=cursor_color, selectforeground=select_colors['foreground'], selectbackground=select_colors['background'], ) IDENTCHARS = string.ascii_letters + string.digits + "_" def colorize_syntax_error(self, text, pos): text.tag_add("ERROR", pos) char = text.get(pos) if char and char in self.IDENTCHARS: text.tag_add("ERROR", pos + " wordstart", pos) if '\n' == text.get(pos): # error at line end text.mark_set("insert", pos) else: text.mark_set("insert", pos + "+1c") text.see(pos) def ResetFont(self): "Update the text widgets' font if it is changed" # Called from configDialog.py fontWeight='normal' if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'): fontWeight='bold' self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'), idleConf.GetOption('main','EditorWindow','font-size', type='int'), fontWeight)) def RemoveKeybindings(self): "Remove the keybindings before they are changed." # Called from configDialog.py self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet() for event, keylist in keydefs.items(): self.text.event_delete(event, *keylist) for extensionName in self.get_standard_extension_names(): xkeydefs = idleConf.GetExtensionBindings(extensionName) if xkeydefs: for event, keylist in xkeydefs.items(): self.text.event_delete(event, *keylist) def ApplyKeybindings(self): "Update the keybindings after they are changed" # Called from configDialog.py self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet() self.apply_bindings() for extensionName in self.get_standard_extension_names(): xkeydefs = idleConf.GetExtensionBindings(extensionName) if xkeydefs: self.apply_bindings(xkeydefs) #update menu accelerators menuEventDict = {} for menu in self.Bindings.menudefs: menuEventDict[menu[0]] = {} for item in menu[1]: if item: menuEventDict[menu[0]][prepstr(item[0])[1]] = item[1] for menubarItem in self.menudict: menu = self.menudict[menubarItem] end = menu.index(END) if end is None: # Skip empty menus continue end += 1 for index in range(0, end): if menu.type(index) == 'command': accel = menu.entrycget(index, 'accelerator') if accel: itemName = menu.entrycget(index, 'label') event = '' if menubarItem in menuEventDict: if itemName in menuEventDict[menubarItem]: event = menuEventDict[menubarItem][itemName] if event: accel = get_accelerator(keydefs, event) menu.entryconfig(index, accelerator=accel) def set_notabs_indentwidth(self): "Update the indentwidth if changed and not using tabs in this window" # Called from configDialog.py if not self.usetabs: self.indentwidth = idleConf.GetOption('main', 'Indent','num-spaces', type='int') def reset_help_menu_entries(self): "Update the additional help entries on the Help menu" help_list = idleConf.GetAllExtraHelpSourcesList() helpmenu = self.menudict['help'] # first delete the extra help entries, if any helpmenu_length = helpmenu.index(END) if helpmenu_length > self.base_helpmenu_length: helpmenu.delete((self.base_helpmenu_length + 1), helpmenu_length) # then rebuild them if help_list: helpmenu.add_separator() for entry in help_list: cmd = self.__extra_help_callback(entry[1]) helpmenu.add_command(label=entry[0], command=cmd) # and update the menu dictionary self.menudict['help'] = helpmenu def __extra_help_callback(self, helpfile): "Create a callback with the helpfile value frozen at definition time" def display_extra_help(helpfile=helpfile): if not helpfile.startswith(('www', 'http')): helpfile = os.path.normpath(helpfile) if sys.platform[:3] == 'win': try: os.startfile(helpfile) except WindowsError as why: tkMessageBox.showerror(title='Document Start Failure', message=str(why), parent=self.text) else: webbrowser.open(helpfile) return display_extra_help def update_recent_files_list(self, new_file=None): "Load and update the recent files list and menus" rf_list = [] if os.path.exists(self.recent_files_path): with open(self.recent_files_path, 'r', encoding='utf_8', errors='replace') as rf_list_file: rf_list = rf_list_file.readlines() if new_file: new_file = os.path.abspath(new_file) + '\n' if new_file in rf_list: rf_list.remove(new_file) # move to top rf_list.insert(0, new_file) # clean and save the recent files list bad_paths = [] for path in rf_list: if '\0' in path or not os.path.exists(path[0:-1]): bad_paths.append(path) rf_list = [path for path in rf_list if path not in bad_paths] ulchars = "1234567890ABCDEFGHIJK" rf_list = rf_list[0:len(ulchars)] try: with open(self.recent_files_path, 'w', encoding='utf_8', errors='replace') as rf_file: rf_file.writelines(rf_list) except OSError as err: if not getattr(self.root, "recentfilelist_error_displayed", False): self.root.recentfilelist_error_displayed = True tkMessageBox.showerror(title='IDLE Error', message='Unable to update Recent Files list:\n%s' % str(err), parent=self.text) # for each edit window instance, construct the recent files menu for instance in self.top.instance_dict: menu = instance.recent_files_menu menu.delete(0, END) # clear, and rebuild: for i, file_name in enumerate(rf_list): file_name = file_name.rstrip() # zap \n # make unicode string to display non-ASCII chars correctly ufile_name = self._filename_to_unicode(file_name) callback = instance.__recent_file_callback(file_name) menu.add_command(label=ulchars[i] + " " + ufile_name, command=callback, underline=0) def __recent_file_callback(self, file_name): def open_recent_file(fn_closure=file_name): self.io.open(editFile=fn_closure) return open_recent_file def saved_change_hook(self): short = self.short_title() long = self.long_title() if short and long: title = short + " - " + long elif short: title = short elif long: title = long else: title = "Untitled" icon = short or long or title if not self.get_saved(): title = "*%s*" % title icon = "*%s" % icon self.top.wm_title(title) self.top.wm_iconname(icon) def get_saved(self): return self.undo.get_saved() def set_saved(self, flag): self.undo.set_saved(flag) def reset_undo(self): self.undo.reset_undo() def short_title(self): pyversion = "Python " + python_version() + ": " filename = self.io.filename if filename: filename = os.path.basename(filename) else: filename = "Untitled" # return unicode string to display non-ASCII chars correctly return pyversion + self._filename_to_unicode(filename) def long_title(self): # return unicode string to display non-ASCII chars correctly return self._filename_to_unicode(self.io.filename or "") def center_insert_event(self, event): self.center() def center(self, mark="insert"): text = self.text top, bot = self.getwindowlines() lineno = self.getlineno(mark) height = bot - top newtop = max(1, lineno - height//2) text.yview(float(newtop)) def getwindowlines(self): text = self.text top = self.getlineno("@0,0") bot = self.getlineno("@0,65535") if top == bot and text.winfo_height() == 1: # Geometry manager hasn't run yet height = int(text['height']) bot = top + height - 1 return top, bot def getlineno(self, mark="insert"): text = self.text return int(float(text.index(mark))) def get_geometry(self): "Return (width, height, x, y)" geom = self.top.wm_geometry() m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom) return list(map(int, m.groups())) def close_event(self, event): self.close() def maybesave(self): if self.io: if not self.get_saved(): if self.top.state()!='normal': self.top.deiconify() self.top.lower() self.top.lift() return self.io.maybesave() def close(self): reply = self.maybesave() if str(reply) != "cancel": self._close() return reply def _close(self): if self.io.filename: self.update_recent_files_list(new_file=self.io.filename) WindowList.unregister_callback(self.postwindowsmenu) self.unload_extensions() self.io.close() self.io = None self.undo = None if self.color: self.color.close(False) self.color = None self.text = None self.tkinter_vars = None self.per.close() self.per = None self.top.destroy() if self.close_hook: # unless override: unregister from flist, terminate if last window self.close_hook() def load_extensions(self): self.extensions = {} self.load_standard_extensions() def unload_extensions(self): for ins in list(self.extensions.values()): if hasattr(ins, "close"): ins.close() self.extensions = {} def load_standard_extensions(self): for name in self.get_standard_extension_names(): try: self.load_extension(name) except: print("Failed to load extension", repr(name)) traceback.print_exc() def get_standard_extension_names(self): return idleConf.GetExtensions(editor_only=True) def load_extension(self, name): try: try: mod = importlib.import_module('.' + name, package=__package__) except ImportError: mod = importlib.import_module(name) except ImportError: print("\nFailed to import extension: ", name) raise cls = getattr(mod, name) keydefs = idleConf.GetExtensionBindings(name) if hasattr(cls, "menudefs"): self.fill_menus(cls.menudefs, keydefs) ins = cls(self) self.extensions[name] = ins if keydefs: self.apply_bindings(keydefs) for vevent in keydefs: methodname = vevent.replace("-", "_") while methodname[:1] == '<': methodname = methodname[1:] while methodname[-1:] == '>': methodname = methodname[:-1] methodname = methodname + "_event" if hasattr(ins, methodname): self.text.bind(vevent, getattr(ins, methodname)) def apply_bindings(self, keydefs=None): if keydefs is None: keydefs = self.Bindings.default_keydefs text = self.text text.keydefs = keydefs for event, keylist in keydefs.items(): if keylist: text.event_add(event, *keylist) def fill_menus(self, menudefs=None, keydefs=None): """Add appropriate entries to the menus and submenus Menus that are absent or None in self.menudict are ignored. """ if menudefs is None: menudefs = self.Bindings.menudefs if keydefs is None: keydefs = self.Bindings.default_keydefs menudict = self.menudict text = self.text for mname, entrylist in menudefs: menu = menudict.get(mname) if not menu: continue for entry in entrylist: if not entry: menu.add_separator() else: label, eventname = entry checkbutton = (label[:1] == '!') if checkbutton: label = label[1:] underline, label = prepstr(label) accelerator = get_accelerator(keydefs, eventname) def command(text=text, eventname=eventname): text.event_generate(eventname) if checkbutton: var = self.get_var_obj(eventname, BooleanVar) menu.add_checkbutton(label=label, underline=underline, command=command, accelerator=accelerator, variable=var) else: menu.add_command(label=label, underline=underline, command=command, accelerator=accelerator) def getvar(self, name): var = self.get_var_obj(name) if var: value = var.get() return value else: raise NameError(name) def setvar(self, name, value, vartype=None): var = self.get_var_obj(name, vartype) if var: var.set(value) else: raise NameError(name) def get_var_obj(self, name, vartype=None): var = self.tkinter_vars.get(name) if not var and vartype: # create a Tkinter variable object with self.text as master: self.tkinter_vars[name] = var = vartype(self.text) return var # Tk implementations of "virtual text methods" -- each platform # reusing IDLE's support code needs to define these for its GUI's # flavor of widget. # Is character at text_index in a Python string? Return 0 for # "guaranteed no", true for anything else. This info is expensive # to compute ab initio, but is probably already known by the # platform's colorizer. def is_char_in_string(self, text_index): if self.color: # Return true iff colorizer hasn't (re)gotten this far # yet, or the character is tagged as being in a string return self.text.tag_prevrange("TODO", text_index) or \ "STRING" in self.text.tag_names(text_index) else: # The colorizer is missing: assume the worst return 1 # If a selection is defined in the text widget, return (start, # end) as Tkinter text indices, otherwise return (None, None) def get_selection_indices(self): try: first = self.text.index("sel.first") last = self.text.index("sel.last") return first, last except TclError: return None, None # Return the text widget's current view of what a tab stop means # (equivalent width in spaces). def get_tk_tabwidth(self): current = self.text['tabs'] or TK_TABWIDTH_DEFAULT return int(current) # Set the text widget's current view of what a tab stop means. def set_tk_tabwidth(self, newtabwidth): text = self.text if self.get_tk_tabwidth() != newtabwidth: # Set text widget tab width pixels = text.tk.call("font", "measure", text["font"], "-displayof", text.master, "n" * newtabwidth) text.configure(tabs=pixels) ### begin autoindent code ### (configuration was moved to beginning of class) def set_indentation_params(self, is_py_src, guess=True): if is_py_src and guess: i = self.guess_indent() if 2 <= i <= 8: self.indentwidth = i if self.indentwidth != self.tabwidth: self.usetabs = False self.set_tk_tabwidth(self.tabwidth) def smart_backspace_event(self, event): text = self.text first, last = self.get_selection_indices() if first and last: text.delete(first, last) text.mark_set("insert", first) return "break" # Delete whitespace left, until hitting a real char or closest # preceding virtual tab stop. chars = text.get("insert linestart", "insert") if chars == '': if text.compare("insert", ">", "1.0"): # easy: delete preceding newline text.delete("insert-1c") else: text.bell() # at start of buffer return "break" if chars[-1] not in " \t": # easy: delete preceding real char text.delete("insert-1c") return "break" # Ick. It may require *inserting* spaces if we back up over a # tab character! This is written to be clear, not fast. tabwidth = self.tabwidth have = len(chars.expandtabs(tabwidth)) assert have > 0 want = ((have - 1) // self.indentwidth) * self.indentwidth # Debug prompt is multilined.... if self.context_use_ps1: last_line_of_prompt = sys.ps1.split('\n')[-1] else: last_line_of_prompt = '' ncharsdeleted = 0 while 1: if chars == last_line_of_prompt: break chars = chars[:-1] ncharsdeleted = ncharsdeleted + 1 have = len(chars.expandtabs(tabwidth)) if have <= want or chars[-1] not in " \t": break text.undo_block_start() text.delete("insert-%dc" % ncharsdeleted, "insert") if have < want: text.insert("insert", ' ' * (want - have)) text.undo_block_stop() return "break" def smart_indent_event(self, event): # if intraline selection: # delete it # elif multiline selection: # do indent-region # else: # indent one level text = self.text first, last = self.get_selection_indices() text.undo_block_start() try: if first and last: if index2line(first) != index2line(last): return self.indent_region_event(event) text.delete(first, last) text.mark_set("insert", first) prefix = text.get("insert linestart", "insert") raw, effective = classifyws(prefix, self.tabwidth) if raw == len(prefix): # only whitespace to the left self.reindent_to(effective + self.indentwidth) else: # tab to the next 'stop' within or to right of line's text: if self.usetabs: pad = '\t' else: effective = len(prefix.expandtabs(self.tabwidth)) n = self.indentwidth pad = ' ' * (n - effective % n) text.insert("insert", pad) text.see("insert") return "break" finally: text.undo_block_stop() def newline_and_indent_event(self, event): text = self.text first, last = self.get_selection_indices() text.undo_block_start() try: if first and last: text.delete(first, last) text.mark_set("insert", first) line = text.get("insert linestart", "insert") i, n = 0, len(line) while i < n and line[i] in " \t": i = i+1 if i == n: # the cursor is in or at leading indentation in a continuation # line; just inject an empty line at the start text.insert("insert linestart", '\n') return "break" indent = line[:i] # strip whitespace before insert point unless it's in the prompt i = 0 last_line_of_prompt = sys.ps1.split('\n')[-1] while line and line[-1] in " \t" and line != last_line_of_prompt: line = line[:-1] i = i+1 if i: text.delete("insert - %d chars" % i, "insert") # strip whitespace after insert point while text.get("insert") in " \t": text.delete("insert") # start new line text.insert("insert", '\n') # adjust indentation for continuations and block # open/close first need to find the last stmt lno = index2line(text.index('insert')) y = PyParse.Parser(self.indentwidth, self.tabwidth) if not self.context_use_ps1: for context in self.num_context_lines: startat = max(lno - context, 1) startatindex = repr(startat) + ".0" rawtext = text.get(startatindex, "insert") y.set_str(rawtext) bod = y.find_good_parse_start( self.context_use_ps1, self._build_char_in_string_func(startatindex)) if bod is not None or startat == 1: break y.set_lo(bod or 0) else: r = text.tag_prevrange("console", "insert") if r: startatindex = r[1] else: startatindex = "1.0" rawtext = text.get(startatindex, "insert") y.set_str(rawtext) y.set_lo(0) c = y.get_continuation_type() if c != PyParse.C_NONE: # The current stmt hasn't ended yet. if c == PyParse.C_STRING_FIRST_LINE: # after the first line of a string; do not indent at all pass elif c == PyParse.C_STRING_NEXT_LINES: # inside a string which started before this line; # just mimic the current indent text.insert("insert", indent) elif c == PyParse.C_BRACKET: # line up with the first (if any) element of the # last open bracket structure; else indent one # level beyond the indent of the line with the # last open bracket self.reindent_to(y.compute_bracket_indent()) elif c == PyParse.C_BACKSLASH: # if more than one line in this stmt already, just # mimic the current indent; else if initial line # has a start on an assignment stmt, indent to # beyond leftmost =; else to beyond first chunk of # non-whitespace on initial line if y.get_num_lines_in_stmt() > 1: text.insert("insert", indent) else: self.reindent_to(y.compute_backslash_indent()) else: assert 0, "bogus continuation type %r" % (c,) return "break" # This line starts a brand new stmt; indent relative to # indentation of initial line of closest preceding # interesting stmt. indent = y.get_base_indent_string() text.insert("insert", indent) if y.is_block_opener(): self.smart_indent_event(event) elif indent and y.is_block_closer(): self.smart_backspace_event(event) return "break" finally: text.see("insert") text.undo_block_stop() # Our editwin provides a is_char_in_string function that works # with a Tk text index, but PyParse only knows about offsets into # a string. This builds a function for PyParse that accepts an # offset. def _build_char_in_string_func(self, startindex): def inner(offset, _startindex=startindex, _icis=self.is_char_in_string): return _icis(_startindex + "+%dc" % offset) return inner def indent_region_event(self, event): head, tail, chars, lines = self.get_region() for pos in range(len(lines)): line = lines[pos] if line: raw, effective = classifyws(line, self.tabwidth) effective = effective + self.indentwidth lines[pos] = self._make_blanks(effective) + line[raw:] self.set_region(head, tail, chars, lines) return "break" def dedent_region_event(self, event): head, tail, chars, lines = self.get_region() for pos in range(len(lines)): line = lines[pos] if line: raw, effective = classifyws(line, self.tabwidth) effective = max(effective - self.indentwidth, 0) lines[pos] = self._make_blanks(effective) + line[raw:] self.set_region(head, tail, chars, lines) return "break" def comment_region_event(self, event): head, tail, chars, lines = self.get_region() for pos in range(len(lines) - 1): line = lines[pos] lines[pos] = '##' + line self.set_region(head, tail, chars, lines) def uncomment_region_event(self, event): head, tail, chars, lines = self.get_region() for pos in range(len(lines)): line = lines[pos] if not line: continue if line[:2] == '##': line = line[2:] elif line[:1] == '#': line = line[1:] lines[pos] = line self.set_region(head, tail, chars, lines) def tabify_region_event(self, event): head, tail, chars, lines = self.get_region() tabwidth = self._asktabwidth() if tabwidth is None: return for pos in range(len(lines)): line = lines[pos] if line: raw, effective = classifyws(line, tabwidth) ntabs, nspaces = divmod(effective, tabwidth) lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:] self.set_region(head, tail, chars, lines) def untabify_region_event(self, event): head, tail, chars, lines = self.get_region() tabwidth = self._asktabwidth() if tabwidth is None: return for pos in range(len(lines)): lines[pos] = lines[pos].expandtabs(tabwidth) self.set_region(head, tail, chars, lines) def toggle_tabs_event(self, event): if self.askyesno( "Toggle tabs", "Turn tabs " + ("on", "off")[self.usetabs] + "?\nIndent width " + ("will be", "remains at")[self.usetabs] + " 8." + "\n Note: a tab is always 8 columns", parent=self.text): self.usetabs = not self.usetabs # Try to prevent inconsistent indentation. # User must change indent width manually after using tabs. self.indentwidth = 8 return "break" # XXX this isn't bound to anything -- see tabwidth comments ## def change_tabwidth_event(self, event): ## new = self._asktabwidth() ## if new != self.tabwidth: ## self.tabwidth = new ## self.set_indentation_params(0, guess=0) ## return "break" def change_indentwidth_event(self, event): new = self.askinteger( "Indent width", "New indent width (2-16)\n(Always use 8 when using tabs)", parent=self.text, initialvalue=self.indentwidth, minvalue=2, maxvalue=16) if new and new != self.indentwidth and not self.usetabs: self.indentwidth = new return "break" def get_region(self): text = self.text first, last = self.get_selection_indices() if first and last: head = text.index(first + " linestart") tail = text.index(last + "-1c lineend +1c") else: head = text.index("insert linestart") tail = text.index("insert lineend +1c") chars = text.get(head, tail) lines = chars.split("\n") return head, tail, chars, lines def set_region(self, head, tail, chars, lines): text = self.text newchars = "\n".join(lines) if newchars == chars: text.bell() return text.tag_remove("sel", "1.0", "end") text.mark_set("insert", head) text.undo_block_start() text.delete(head, tail) text.insert(head, newchars) text.undo_block_stop() text.tag_add("sel", head, "insert") # Make string that displays as n leading blanks. def _make_blanks(self, n): if self.usetabs: ntabs, nspaces = divmod(n, self.tabwidth) return '\t' * ntabs + ' ' * nspaces else: return ' ' * n # Delete from beginning of line to insert point, then reinsert # column logical (meaning use tabs if appropriate) spaces. def reindent_to(self, column): text = self.text text.undo_block_start() if text.compare("insert linestart", "!=", "insert"): text.delete("insert linestart", "insert") if column: text.insert("insert", self._make_blanks(column)) text.undo_block_stop() def _asktabwidth(self): return self.askinteger( "Tab width", "Columns per tab? (2-16)", parent=self.text, initialvalue=self.indentwidth, minvalue=2, maxvalue=16) # Guess indentwidth from text content. # Return guessed indentwidth. This should not be believed unless # it's in a reasonable range (e.g., it will be 0 if no indented # blocks are found). def guess_indent(self): opener, indented = IndentSearcher(self.text, self.tabwidth).run() if opener and indented: raw, indentsmall = classifyws(opener, self.tabwidth) raw, indentlarge = classifyws(indented, self.tabwidth) else: indentsmall = indentlarge = 0 return indentlarge - indentsmall # "line.col" -> line, as an int def index2line(index): return int(float(index)) # Look at the leading whitespace in s. # Return pair (# of leading ws characters, # effective # of leading blanks after expanding # tabs to width tabwidth) def classifyws(s, tabwidth): raw = effective = 0 for ch in s: if ch == ' ': raw = raw + 1 effective = effective + 1 elif ch == '\t': raw = raw + 1 effective = (effective // tabwidth + 1) * tabwidth else: break return raw, effective import tokenize _tokenize = tokenize del tokenize class IndentSearcher(object): # .run() chews over the Text widget, looking for a block opener # and the stmt following it. Returns a pair, # (line containing block opener, line containing stmt) # Either or both may be None. def __init__(self, text, tabwidth): self.text = text self.tabwidth = tabwidth self.i = self.finished = 0 self.blkopenline = self.indentedline = None def readline(self): if self.finished: return "" i = self.i = self.i + 1 mark = repr(i) + ".0" if self.text.compare(mark, ">=", "end"): return "" return self.text.get(mark, mark + " lineend+1c") def tokeneater(self, type, token, start, end, line, INDENT=_tokenize.INDENT, NAME=_tokenize.NAME, OPENERS=('class', 'def', 'for', 'if', 'try', 'while')): if self.finished: pass elif type == NAME and token in OPENERS: self.blkopenline = line elif type == INDENT and self.blkopenline: self.indentedline = line self.finished = 1 def run(self): save_tabsize = _tokenize.tabsize _tokenize.tabsize = self.tabwidth try: try: tokens = _tokenize.generate_tokens(self.readline) for token in tokens: self.tokeneater(*token) except (_tokenize.TokenError, SyntaxError): # since we cut off the tokenizer early, we can trigger # spurious errors pass finally: _tokenize.tabsize = save_tabsize return self.blkopenline, self.indentedline ### end autoindent code ### def prepstr(s): # Helper to extract the underscore from a string, e.g. # prepstr("Co_py") returns (2, "Copy"). i = s.find('_') if i >= 0: s = s[:i] + s[i+1:] return i, s keynames = { 'bracketleft': '[', 'bracketright': ']', 'slash': '/', } def get_accelerator(keydefs, eventname): keylist = keydefs.get(eventname) # issue10940: temporary workaround to prevent hang with OS X Cocoa Tk 8.5 # if not keylist: if (not keylist) or (macosxSupport.runningAsOSXApp() and eventname in { "<
>", "<
>", "<
>"}): return "" s = keylist[0] s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s) s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s) s = re.sub("Key-", "", s) s = re.sub("Cancel","Ctrl-Break",s) # dscherer@cmu.edu s = re.sub("Control-", "Ctrl-", s) s = re.sub("-", "+", s) s = re.sub("><", " ", s) s = re.sub("<", "", s) s = re.sub(">", "", s) return s def fixwordbreaks(root): # Make sure that Tk's double-click and next/previous word # operations use our definition of a word (i.e. an identifier) tk = root.tk tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]') tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]') def test(): root = Tk() fixwordbreaks(root) root.withdraw() if sys.argv[1:]: filename = sys.argv[1] else: filename = None edit = EditorWindow(root=root, filename=filename) edit.set_close_hook(root.quit) edit.text.bind("<
>", edit.close_event) root.mainloop() root.destroy() if __name__ == '__main__': test() idle_test/test_pathbrowser.py 0000644 00000000530 15217643351 0012476 0 ustar 00 import unittest import idlelib.PathBrowser as PathBrowser class PathBrowserTest(unittest.TestCase): def test_DirBrowserTreeItem(self): # Issue16226 - make sure that getting a sublist works d = PathBrowser.DirBrowserTreeItem('') d.GetSubList() if __name__ == '__main__': unittest.main(verbosity=2, exit=False) idle_test/test_config_name.py 0000644 00000004537 15217643351 0012416 0 ustar 00 """Unit tests for idlelib.configSectionNameDialog""" import unittest from idlelib.idle_test.mock_tk import Var, Mbox from idlelib import configSectionNameDialog as name_dialog_module name_dialog = name_dialog_module.GetCfgSectionNameDialog class Dummy_name_dialog: # Mock for testing the following methods of name_dialog name_ok = name_dialog.name_ok Ok = name_dialog.Ok Cancel = name_dialog.Cancel # Attributes, constant or variable, needed for tests used_names = ['used'] name = Var() result = None destroyed = False def destroy(self): self.destroyed = True # name_ok calls Mbox.showerror if name is not ok orig_mbox = name_dialog_module.tkMessageBox showerror = Mbox.showerror class ConfigNameTest(unittest.TestCase): dialog = Dummy_name_dialog() @classmethod def setUpClass(cls): name_dialog_module.tkMessageBox = Mbox @classmethod def tearDownClass(cls): name_dialog_module.tkMessageBox = orig_mbox def test_blank_name(self): self.dialog.name.set(' ') self.assertEqual(self.dialog.name_ok(), '') self.assertEqual(showerror.title, 'Name Error') self.assertIn('No', showerror.message) def test_used_name(self): self.dialog.name.set('used') self.assertEqual(self.dialog.name_ok(), '') self.assertEqual(showerror.title, 'Name Error') self.assertIn('use', showerror.message) def test_long_name(self): self.dialog.name.set('good'*8) self.assertEqual(self.dialog.name_ok(), '') self.assertEqual(showerror.title, 'Name Error') self.assertIn('too long', showerror.message) def test_good_name(self): self.dialog.name.set(' good ') showerror.title = 'No Error' # should not be called self.assertEqual(self.dialog.name_ok(), 'good') self.assertEqual(showerror.title, 'No Error') def test_ok(self): self.dialog.destroyed = False self.dialog.name.set('good') self.dialog.Ok() self.assertEqual(self.dialog.result, 'good') self.assertTrue(self.dialog.destroyed) def test_cancel(self): self.dialog.destroyed = False self.dialog.Cancel() self.assertEqual(self.dialog.result, '') self.assertTrue(self.dialog.destroyed) if __name__ == '__main__': unittest.main(verbosity=2, exit=False) idle_test/test_text.py 0000644 00000015150 15217643351 0011126 0 ustar 00 # Test mock_tk.Text class against tkinter.Text class by running same tests with both. import unittest from test.support import requires from _tkinter import TclError import tkinter as tk class TextTest(object): hw = 'hello\nworld' # usual initial insert after initialization hwn = hw+'\n' # \n present at initialization, before insert Text = None def setUp(self): self.text = self.Text() def test_init(self): self.assertEqual(self.text.get('1.0'), '\n') self.assertEqual(self.text.get('end'), '') def test_index_empty(self): index = self.text.index for dex in (-1.0, 0.3, '1.-1', '1.0', '1.0 lineend', '1.end', '1.33', 'insert'): self.assertEqual(index(dex), '1.0') for dex in 'end', 2.0, '2.1', '33.44': self.assertEqual(index(dex), '2.0') def test_index_data(self): index = self.text.index self.text.insert('1.0', self.hw) for dex in -1.0, 0.3, '1.-1', '1.0': self.assertEqual(index(dex), '1.0') for dex in '1.0 lineend', '1.end', '1.33': self.assertEqual(index(dex), '1.5') for dex in 'end', '33.44': self.assertEqual(index(dex), '3.0') def test_get(self): get = self.text.get Equal = self.assertEqual self.text.insert('1.0', self.hw) Equal(get('end'), '') Equal(get('end', 'end'), '') Equal(get('1.0'), 'h') Equal(get('1.0', '1.1'), 'h') Equal(get('1.0', '1.3'), 'hel') Equal(get('1.1', '1.3'), 'el') Equal(get('1.0', '1.0 lineend'), 'hello') Equal(get('1.0', '1.10'), 'hello') Equal(get('1.0 lineend'), '\n') Equal(get('1.1', '2.3'), 'ello\nwor') Equal(get('1.0', '2.5'), self.hw) Equal(get('1.0', 'end'), self.hwn) Equal(get('0.0', '5.0'), self.hwn) def test_insert(self): insert = self.text.insert get = self.text.get Equal = self.assertEqual insert('1.0', self.hw) Equal(get('1.0', 'end'), self.hwn) insert('1.0', '') # nothing Equal(get('1.0', 'end'), self.hwn) insert('1.0', '*') Equal(get('1.0', 'end'), '*hello\nworld\n') insert('1.0 lineend', '*') Equal(get('1.0', 'end'), '*hello*\nworld\n') insert('2.3', '*') Equal(get('1.0', 'end'), '*hello*\nwor*ld\n') insert('end', 'x') Equal(get('1.0', 'end'), '*hello*\nwor*ldx\n') insert('1.4', 'x\n') Equal(get('1.0', 'end'), '*helx\nlo*\nwor*ldx\n') def test_no_delete(self): # if index1 == 'insert' or 'end' or >= end, there is no deletion delete = self.text.delete get = self.text.get Equal = self.assertEqual self.text.insert('1.0', self.hw) delete('insert') Equal(get('1.0', 'end'), self.hwn) delete('end') Equal(get('1.0', 'end'), self.hwn) delete('insert', 'end') Equal(get('1.0', 'end'), self.hwn) delete('insert', '5.5') Equal(get('1.0', 'end'), self.hwn) delete('1.4', '1.0') Equal(get('1.0', 'end'), self.hwn) delete('1.4', '1.4') Equal(get('1.0', 'end'), self.hwn) def test_delete_char(self): delete = self.text.delete get = self.text.get Equal = self.assertEqual self.text.insert('1.0', self.hw) delete('1.0') Equal(get('1.0', '1.end'), 'ello') delete('1.0', '1.1') Equal(get('1.0', '1.end'), 'llo') # delete \n and combine 2 lines into 1 delete('1.end') Equal(get('1.0', '1.end'), 'lloworld') self.text.insert('1.3', '\n') delete('1.10') Equal(get('1.0', '1.end'), 'lloworld') self.text.insert('1.3', '\n') delete('1.3', '2.0') Equal(get('1.0', '1.end'), 'lloworld') def test_delete_slice(self): delete = self.text.delete get = self.text.get Equal = self.assertEqual self.text.insert('1.0', self.hw) delete('1.0', '1.0 lineend') Equal(get('1.0', 'end'), '\nworld\n') delete('1.0', 'end') Equal(get('1.0', 'end'), '\n') self.text.insert('1.0', self.hw) delete('1.0', '2.0') Equal(get('1.0', 'end'), 'world\n') delete('1.0', 'end') Equal(get('1.0', 'end'), '\n') self.text.insert('1.0', self.hw) delete('1.2', '2.3') Equal(get('1.0', 'end'), 'held\n') def test_multiple_lines(self): # insert and delete self.text.insert('1.0', 'hello') self.text.insert('1.3', '1\n2\n3\n4\n5') self.assertEqual(self.text.get('1.0', 'end'), 'hel1\n2\n3\n4\n5lo\n') self.text.delete('1.3', '5.1') self.assertEqual(self.text.get('1.0', 'end'), 'hello\n') def test_compare(self): compare = self.text.compare Equal = self.assertEqual # need data so indexes not squished to 1,0 self.text.insert('1.0', 'First\nSecond\nThird\n') self.assertRaises(TclError, compare, '2.2', 'op', '2.2') for op, less1, less0, equal, greater0, greater1 in ( ('<', True, True, False, False, False), ('<=', True, True, True, False, False), ('>', False, False, False, True, True), ('>=', False, False, True, True, True), ('==', False, False, True, False, False), ('!=', True, True, False, True, True), ): Equal(compare('1.1', op, '2.2'), less1, op) Equal(compare('2.1', op, '2.2'), less0, op) Equal(compare('2.2', op, '2.2'), equal, op) Equal(compare('2.3', op, '2.2'), greater0, op) Equal(compare('3.3', op, '2.2'), greater1, op) class MockTextTest(TextTest, unittest.TestCase): @classmethod def setUpClass(cls): from idlelib.idle_test.mock_tk import Text cls.Text = Text def test_decode(self): # test endflags (-1, 0) not tested by test_index (which uses +1) decode = self.text._decode Equal = self.assertEqual self.text.insert('1.0', self.hw) Equal(decode('end', -1), (2, 5)) Equal(decode('3.1', -1), (2, 5)) Equal(decode('end', 0), (2, 6)) Equal(decode('3.1', 0), (2, 6)) class TkTextTest(TextTest, unittest.TestCase): @classmethod def setUpClass(cls): requires('gui') from tkinter import Tk, Text cls.Text = Text cls.root = Tk() @classmethod def tearDownClass(cls): cls.root.destroy() del cls.root if __name__ == '__main__': unittest.main(verbosity=2, exit=False) idle_test/__pycache__/test_formatparagraph.cpython-33.pyo 0000644 00000042442 15217643351 0017621 0 ustar 00 f7 c @ s= d d l Z d d l m Z d d l m Z d d l m Z m Z m Z d d l m Z Gd d d e j Z Gd d d e j Z Gd d d e j Z Gd d d e j Z Gd d d e j Z Gd d d Z Gd d d Z Gd d d e j Z e d k r9e j d d d d n d S( i N( u FormatParagraph( u EditorWindow( u Tku Textu TclError( u requiresc B s\ | Ee Z d Z d Z d Z d Z d Z d Z d Z d d Z d d Z d d Z d S( u Is_Get_Testu Test the is_ and get_ functionsu # This is a commentu This is not a commentu # This is a comment u # This is a commentu This is not a commentc C sI | j t j d | j t j d | j t j | j d S( Nu u ( u assertTrueu fpu is_all_whiteu assertFalseu test_comment( u self( ( uK /opt/alt/python33/lib64/python3.3/idlelib/idle_test/test_formatparagraph.pyu test_is_all_white s u Is_Get_Test.test_is_all_whitec C sq | j } | t j | j d | t j | j d | t j | j d | t j | j d d S( Nu u ( u assertEqualu fpu get_indentu test_commentu trailingws_commentu leadingws_commentu leadingws_nocomment( u selfu Equal( ( uK /opt/alt/python33/lib64/python3.3/idlelib/idle_test/test_formatparagraph.pyu test_get_indent s u Is_Get_Test.test_get_indentc C s | j } | t j | j d | t j | j d | t j | j d | t j | j d | t j | j d d S( Nu #u #u u ( u assertEqualu fpu get_comment_headeru test_commentu trailingws_commentu leadingws_commentu leadingws_nocommentu test_nocomment( u selfu Equal( ( uK /opt/alt/python33/lib64/python3.3/idlelib/idle_test/test_formatparagraph.pyu test_get_comment_header s u# Is_Get_Test.test_get_comment_headerN( u __name__u __module__u __qualname__u __doc__u test_commentu test_nocommentu trailingws_commentu leadingws_commentu leadingws_nocommentu test_is_all_whiteu test_get_indentu test_get_comment_header( u __locals__( ( uK /opt/alt/python33/lib64/python3.3/idlelib/idle_test/test_formatparagraph.pyu Is_Get_Test s u Is_Get_Testc B sP | Ee Z d Z d Z e d d Z d d Z d d Z d d Z d S( u FindTestu` Test the find_paragraph function in FormatParagraph. Using the runcase() function, find_paragraph() is called with 'mark' set at multiple indexes before and inside the test paragraph. It appears that code with the same indentation as a quoted string is grouped as part of the same paragraph, which is probably incorrect behavior. c C s d d l m } | | _ d S( Ni ( u Text( u idlelib.idle_test.mock_tku Textu text( u clsu Text( ( uK /opt/alt/python33/lib64/python3.3/idlelib/idle_test/test_formatparagraph.pyu setUpClass2 s u FindTest.setUpClassc C s | j } | j d | x t d | D]v } t | j d | j d d } xG d | d | f D]2 } d | | f } | j t j | | | qi Wq) W| j d d d S( Nu 1.0i u %d.endu .i i u %d.%du end( u textu insertu rangeu intu indexu splitu assertEqualu fpu find_paragraphu delete( u selfu inserttextu stoplineu expectedu textu lineu linelengthu colu tempindex( ( uK /opt/alt/python33/lib64/python3.3/idlelib/idle_test/test_formatparagraph.pyu runcase7 s &$u FindTest.runcasec C s| d } | j | d d d d | d d f d } | j | d d d d | d d f d } | j | d d d d | d d f d } | j | d d d d | d d f d } | j | d d d d | d d f d } | j | d d d d | d d f d } | j | d d d d | d d f d } | j | d d d d | d d f d S( Nu; # Comment block with no blank lines before # Comment line i u 1.0u 3.0u #i i: uG # Comment block with whitespace line before and after # Comment line i u 2.0u 4.0i iF uS # Indented comment block with whitespace before and after # Comment line u #iR u # Single line comment i u4 # Single line comment with leading whitespace i3 u0 # Comment immediately followed by code x = 42 i( u= # Indented comment immediately followed by code x = 42 i5 u= # Comment immediately followed by indented code x = 42 i1 ( u runcase( u selfu comment( ( uK /opt/alt/python33/lib64/python3.3/idlelib/idle_test/test_formatparagraph.pyu test_find_commentD s )))))))u FindTest.test_find_commentc C s d } | j | d d d d | d d f d } | j | d d d d | d d f d } | j | d d d d | d d f d } | j | d d d d | d d f d } | j | d d d d | d d f d S( Nu6 """String with no blank lines before String line """ i u 1.0u 4.0u i i5 uC """String with whitespace line before and after String line. """ i u 2.0u 5.0i iB uV """Indented string with whitespace before and after Comment string. """ u iU u """Single line string.""" i u 3.0i u8 """Single line string with leading whitespace.""" i7 ( u runcase( u selfu teststring( ( uK /opt/alt/python33/lib64/python3.3/idlelib/idle_test/test_formatparagraph.pyu test_find_paragraphz s ))))u FindTest.test_find_paragraphN( u __name__u __module__u __qualname__u __doc__u classmethodu setUpClassu runcaseu test_find_commentu test_find_paragraph( u __locals__( ( uK /opt/alt/python33/lib64/python3.3/idlelib/idle_test/test_formatparagraph.pyu FindTest( s 6u FindTestc B s&