def print_window(self, event): m = tkMessageBox.Message(title="Print", message="Print to Default Printer", icon=tkMessageBox.QUESTION, type=tkMessageBox.OKCANCEL, default=tkMessageBox.OK, master=self.text) reply = m.show() if reply != tkMessageBox.OK: self.text.focus_set() return "break" tempfilename = None saved = self.get_saved() if saved: filename = self.filename # shell undo is reset after every prompt, looks saved, probably isn't if not saved or filename is None: (tfd, tempfilename) = tempfile.mkstemp(prefix='IDLE_tmp_') filename = tempfilename os.close(tfd) if not self.writefile(tempfilename): os.unlink(tempfilename) return "break" platform = os.name printPlatform = 1 if platform == 'posix': #posix platform command = idleConf.GetOption('main', 'General', 'print-command-posix') command = command + " 2>&1" elif platform == 'nt': #win32 platform command = idleConf.GetOption('main', 'General', 'print-command-win') else: #no printing for this platform printPlatform = 0 if printPlatform: #we can try to print for this platform command = command % filename pipe = os.popen(command, "r") # things can get ugly on NT if there is no printer available. output = pipe.read().strip() status = pipe.close() if status: output = "Printing failed (exit status 0x%x)\n" % \ status + output if output: output = "Printing command: %s\n" % repr(command) + output tkMessageBox.showerror("Print status", output, master=self.text) else: #no printing for this platform message = "Printing is not enabled for this platform: %s" % platform tkMessageBox.showinfo("Print status", message, master=self.text) if tempfilename: os.unlink(tempfilename) return "break"
def ResetFont(self): "Update the text widgets' font if it is changed" # Called from configDialog.py fontWeight = 'normal' if idleConf.GetOption('main', 'EditorPage', 'font-bold', type='bool'): fontWeight = 'bold' for page in self.text_notebook.pages.itervalues(): text = page.editpage.text text.config( font=(idleConf.GetOption('main', 'EditorPage', 'font'), idleConf.GetOption('main', 'EditorPage', 'font-size'), fontWeight))
def getfilename(self): """Get source filename. If not saved, offer to save (or create) file The debugger requires a source file. Make sure there is one, and that the current version of the source buffer has been saved. If the user declines to save or cancels the Save As dialog, return None. If the user has configured IDLE for Autosave, the file will be silently saved if it already exists and is dirty. """ filename = self.editwin.io.filename is_temp = False if not self.editwin.get_saved(): autosave = idleConf.GetOption('main', 'General', 'autosave', type='bool') save_before_run = idleConf.GetOption('main', 'General', 'save-before-run', default=1, type='bool') io = self.editwin.io if filename and autosave: # This has been saved before and IDLE is configured to not # prompt before saving this file again. io.save(None) elif filename or save_before_run: # This has been saved before and IDLE is configured to prompt # before saving this file again, or, this has never been # saved and the configuration tells it must be saved. reply = self.ask_save_dialog() self.editwin.text.focus_set() if reply == "ok": io.save(None) filename = self.editwin.io.filename else: filename = None else: # This has never been saved before but IDLE is configured # to allow running the present code without explicitly # saving. if not save_before_run: filename = io.save_as_temp(prefix='IDLE_rtmp_') is_temp = True return filename, is_temp
def drawtext(self): textx = self.x+20-1 texty = self.y-1 labeltext = self.item.GetLabelText() if labeltext: id = self.canvas.create_text(textx, texty, anchor="nw", text=labeltext) self.canvas.tag_bind(id, "<1>", self.select) self.canvas.tag_bind(id, "<Double-1>", self.flip) x0, y0, x1, y1 = self.canvas.bbox(id) textx = max(x1, 200) + 10 text = self.item.GetText() or "<no text>" try: self.entry except AttributeError: pass else: self.edit_finish() try: label = self.label except AttributeError: # padding carefully selected (on Windows) to match Entry widget: self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2) theme = idleConf.GetOption('main','Theme','name') if self.selected: self.label.configure(idleConf.GetHighlight(theme, 'hilite')) else: self.label.configure(idleConf.GetHighlight(theme, 'normal')) id = self.canvas.create_window(textx, texty, anchor="nw", window=self.label) self.label.bind("<1>", self.select_or_edit) self.label.bind("<Double-1>", self.flip) self.text_id = id
def getfilename(self): """Get source filename. If not saved, offer to save (or create) file The debugger requires a source file. Make sure there is one, and that the current version of the source buffer has been saved. If the user declines to save or cancels the Save As dialog, return None. If the user has configured IDLE for Autosave, the file will be silently saved if it already exists and is dirty. """ page = self.editpage filename = page.io.filename if not page.get_saved(): autosave = idleConf.GetOption('main', 'General', 'autosave', type='bool') if autosave and filename: page.io.save(None) else: reply = self.ask_save_dialog() page.text.focus_set() if reply == "ok": page.io.save(None) filename = page.io.filename else: filename = None return filename
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 __init__(self, text, output_sep="\n"): self.text = text self.history = [] self.history_prefix = None self.history_pointer = None self.output_sep = output_sep self.cyclic = idleConf.GetOption("main", "History", "cyclic", 1, "bool") text.bind("<<history-previous>>", self.history_prev) text.bind("<<history-next>>", self.history_next)
def __new__(cls, name, bases, dct): newcls = type.__new__(cls, name, bases, dct) if name != 'EnablableExtension': idleConf.GetOption("extensions", name, "enable", type="bool", default=True, member_name='enable') return newcls
def __init__(self, parent, filename): "Configure tags and feed file to parser." uwide = idleConf.GetOption('main', 'EditorWindow', 'width', type='int') uhigh = idleConf.GetOption('main', 'EditorWindow', 'height', type='int') uhigh = 3 * uhigh // 4 # lines average 4/3 of editor line height Text.__init__(self, parent, wrap='word', highlightthickness=0, padx=5, borderwidth=0, width=uwide, height=uhigh) normalfont = self.findfont(['TkDefaultFont', 'arial', 'helvetica']) fixedfont = self.findfont(['TkFixedFont', 'monaco', 'courier']) self['font'] = (normalfont, 12) self.tag_configure('em', font=(normalfont, 12, 'italic')) self.tag_configure('h1', font=(normalfont, 20, 'bold')) self.tag_configure('h2', font=(normalfont, 18, 'bold')) self.tag_configure('h3', font=(normalfont, 15, 'bold')) self.tag_configure('pre', font=(fixedfont, 12), background='#f6f6ff') self.tag_configure('preblock', font=(fixedfont, 10), lmargin1=25, borderwidth=1, relief='solid', background='#eeffcc') self.tag_configure('l1', lmargin1=25, lmargin2=25) self.tag_configure('l2', lmargin1=50, lmargin2=50) self.tag_configure('l3', lmargin1=75, lmargin2=75) self.tag_configure('l4', lmargin1=100, lmargin2=100) self.parser = HelpParser(self) with open(filename, encoding='utf-8') as f: contents = f.read() self.parser.feed(contents) self['state'] = 'disabled'
def LoadTagDefs(self): ColorDelegator.LoadTagDefs(self) theme = idleConf.GetOption('main', 'Theme', 'name') self.tagdefs.update({ "stdin": { 'background': None, 'foreground': None }, "stdout": idleConf.GetHighlight(theme, "stdout"), "stderr": idleConf.GetHighlight(theme, "stderr"), "console": idleConf.GetHighlight(theme, "console"), })
def __init__(self, parent_frame, editwin, title=None, **kwargs): self.editwin = editwin self.title = title self.tab_initialized = False kwargs.setdefault('width', idleConf.GetOption('main', 'EditorPage', 'width')) kwargs.setdefault('height', idleConf.GetOption('main', 'EditorPage', 'height')) self.text = MultiCallCreator(Text)(parent_frame, **kwargs) self.color = None # initialized in reset_colorizer self.per = Percolator(self.text) self.undo = self.editwin.UndoDelegator() self.per.insertfilter(self.undo) self.text.undo_block_start = self.undo.undo_block_start self.text.undo_block_stop = self.undo.undo_block_stop self.io = IOBinding.IOBinding(self) self.undo.set_saved_change_hook(self.saved_change_hook) self.io.set_filename_change_hook(self.filename_change_hook) self.reset_colorizer() self._setup_bindings()
def LoadTagDefs(self): theme = idleConf.GetOption('main','Theme','name') self.tagdefs = { "COMMENT": idleConf.GetHighlight(theme, "comment"), "KEYWORD": idleConf.GetHighlight(theme, "keyword"), "STRING": idleConf.GetHighlight(theme, "string"), "DEFINITION": idleConf.GetHighlight(theme, "definition"), "SYNC": {'background':None,'foreground':None}, "TODO": {'background':None,'foreground':None}, "BREAK": idleConf.GetHighlight(theme, "break"), "ERROR": idleConf.GetHighlight(theme, "error"), # The following is used by ReplaceDialog: "hit": idleConf.GetHighlight(theme, "hit"), }
def reset_colorizer(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'])
def remote_stack_viewer(self): import RemoteObjectBrowser oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist", ), {}) if oid is None: self.tkconsole.root.bell() return item = RemoteObjectBrowser.StubObjectTreeItem(self.rpcclt, oid) from TreeWidget import ScrolledCanvas, TreeNode top = Toplevel(self.tkconsole.root) theme = idleConf.GetOption('main', 'Theme', 'name') background = idleConf.GetHighlight(theme, 'normal')['background'] sc = ScrolledCanvas(top, bg=background, highlightthickness=0) sc.frame.pack(expand=1, fill="both") node = TreeNode(sc.canvas, None, item) node.expand()
def format_paragraph_event(self, event): maxformatwidth = int(idleConf.GetOption('main', 'FormatParagraph', 'paragraph')) text = self.editpage.text first, last = self.editpage.get_selection_indices() if first and last: data = text.get(first, last) comment_header = '' else: first, last, comment_header, data = find_paragraph(text, text.index("insert")) if comment_header: # Reformat the comment lines - convert to text sans header. lines = data.split("\n") lines = map(lambda st, l=len(comment_header): st[l:], lines) data = "\n".join(lines) # Reformat to maxformatwidth chars or a 20 char width, whichever is # greater. format_width = max(maxformatwidth - len(comment_header), 20) newdata = reformat_paragraph(data, format_width) # re-split and re-insert the comment header. newdata = newdata.split("\n") # If the block ends in a \n, we dont want the comment # prefix inserted after it. (Im not sure it makes sense to # reformat a comment block that isnt made of complete # lines, but whatever!) Can't think of a clean soltution, # so we hack away block_suffix = "" if not newdata[-1]: block_suffix = "\n" newdata = newdata[:-1] builder = lambda item, prefix=comment_header: prefix+item newdata = '\n'.join(map(builder, newdata)) + block_suffix else: # Just a normal text format newdata = reformat_paragraph(data, maxformatwidth) text.tag_remove("sel", "1.0", "end") if newdata != data: text.mark_set("insert", first) text.undo_block_start() text.delete(first, last) text.insert(first, newdata) text.undo_block_stop() else: text.mark_set("insert", last) text.see("insert") return "break"
def open(self, event=None, editFile=None): if self.editwin.flist: if not editFile: filename = self.askopenfile() else: filename = editFile if filename: # If the current window has no filename and hasn't been # modified, we replace its contents (no loss). Otherwise # we open a new window, or maybe open in a tab. # But we won't replace the shell window (which has an # interp(reter) attribute), which gets set to "not modified" # at every new prompt. try: interp = self.editwin.interp except AttributeError: interp = None if not self.filename and self.get_saved() and not interp: self.editwin.flist.open(filename, self.loadfile) else: if idleConf.GetOption('main', 'EditorWindow', 'file-in-tab', default=1, type='bool'): self.editwin.flist.open(filename) else: self.text.focus_set() return "break" # # Code for use outside IDLE: if self.get_saved(): reply = self.maybesave() if reply == "cancel": self.text.focus_set() return "break" if not editFile: filename = self.askopenfile() else: filename = editFile if filename: self.loadfile(filename) else: self.text.focus_set() return "break"
def __init__(self, text): '''Initialize data attributes and bind event methods. .text - Idle wrapper of tk Text widget, with .bell(). .history - source statements, possibly with multiple lines. .prefix - source already entered at prompt; filters history list. .pointer - index into history. .cyclic - wrap around history list (or not). ''' self.text = text self.history = [] self.prefix = None self.pointer = None self.cyclic = idleConf.GetOption("main", "History", "cyclic", 1, "bool") text.bind("<<history-previous>>", self.history_prev) text.bind("<<history-next>>", self.history_next)
def __init__(self, editwin): self.editwin = editwin self.text = editwin.text self.textfont = self.text["font"] self.label = None # Dummy line, which starts the "block" of the whole document: self.info = list(self.interesting_lines(1)) self.lastfirstline = 1 visible = idleConf.GetOption("extensions", "CodeContext", "visible", type="bool", default=False) if visible: self.toggle_code_context_event() self.editwin.setvar('<<toggle-code-context>>', 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 init(self, flist): self.flist = flist # reset pyclbr pyclbr._modules.clear() # create top self.top = top = ListedToplevel(flist.root) top.protocol("WM_DELETE_WINDOW", self.close) top.bind("<Escape>", self.close) self.settitle() top.focus_set() # create scrolled canvas theme = idleConf.GetOption('main','Theme','name') background = idleConf.GetHighlight(theme, 'normal')['background'] sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1) sc.frame.pack(expand=1, fill="both") item = self.rootnode() self.node = node = TreeNode(sc.canvas, None, item) node.update() node.expand()
def __init__(self, editpage): self.editpage = editpage self.editwin = editpage.editwin 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('<<toggle-code-context>>', True)
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('<<toggle-code-context>>', 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 build_subprocess_arglist(self): w = ['-W' + s for s in sys.warnoptions] # Maybe IDLE is installed and is being accessed via sys.path, # or maybe it's not installed and the idle.py script is being # run from the IDLE source directory. del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc', default=False, type='bool') if __name__ == 'idlelib.PyShell': command = "__import__('idlelib.run').run.main(" + ` del_exitf ` + ")" else: command = "__import__('run').main(" + ` del_exitf ` + ")" if sys.platform == 'win32' and ' ' in sys.executable: # handle embedded space in path by quoting the argument decorated_exec = '"%s"' % sys.executable else: decorated_exec = sys.executable return [decorated_exec] + w + ["-c", command, str(self.port)]
def open(self, filename, action=None): assert filename filename = _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 self.dict.has_key(key): 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=filename) elif idleConf.GetOption('main', 'EditorWindow', 'file-in-tab', default=1, type='bool'): # check if there is some PyShellEditorWindow running to open this # new tab for entry in self.inversedict: if hasattr(entry, 'set_breakpoint'): # PyShellEditorWindow, good entry.new_tab(filename=filename) # select the last page created entry.text_notebook.select( len(entry.text_notebook.pages) - 1) return entry else: # no PyShellEditorWindows, create one return self.EditorWindow(self, filename, key) else: return self.EditorWindow(self, filename, key)
class AutoComplete: menudefs = [('edit', [ ("Show Completions", "<<force-open-completions>>"), ])] popupwait = idleConf.GetOption("extensions", "AutoComplete", "popupwait", type="int", default=0) def __init__(self, editwin=None): if editwin == None: # subprocess and test self.editwin = None return self.editwin = editwin 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 neccesary, 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 == os.sep: 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 neccesary, 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): self._remove_autocomplete_window() mode = COMPLETE_FILES while i and curline[i - 1] in FILENAME_CHARS: i -= 1 comp_start = curline[i:j] j = i while i and curline[i - 1] in FILENAME_CHARS + os.sep: 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: 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() self.autocompletewindow.show_window(comp_lists, "insert-%dc" % len(comp_start), complete, mode, userWantsWin) return True 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 = eval("__all__", namespace) smalll.sort() else: smalll = filter(lambda s: s[:1] != '_', bigl) else: try: entity = self.get_entity(what) bigl = dir(entity) bigl.sort() if "__all__" in bigl: smalll = entity.__all__ smalll.sort() else: smalll = filter(lambda s: s[:1] != '_', bigl) except: return [], [] elif mode == COMPLETE_FILES: if what == "": what = "." try: expandedpath = os.path.expanduser(what) bigl = os.listdir(expandedpath) bigl.sort() smalll = filter(lambda s: s[:1] != '.', bigl) 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)
elif args: enable_edit = True pathx = [] for filename in args: pathx.append(os.path.dirname(filename)) for dir in pathx: dir = os.path.abspath(dir) if not dir in sys.path: sys.path.insert(0, dir) else: dir = os.getcwd() if not dir in sys.path: sys.path.insert(0, dir) # check the IDLE settings configuration (but command line overrides) edit_start = idleConf.GetOption('main', 'General', 'editor-on-startup', type='bool') enable_edit = enable_edit or edit_start enable_shell = enable_shell or not edit_start # start editor and/or shell windows: root = Tk(className="Idle") fixwordbreaks(root) root.withdraw() flist = PyShellFileList(root) if enable_edit: if not (cmd or script): for filename in args: flist.open(filename) if not args: flist.new() if enable_shell:
if self.fileencoding == BOM_UTF8 or failed: return BOM_UTF8 + chars.encode("utf-8") # Try the original file encoding next, if any if self.fileencoding: try: return chars.encode(self.fileencoding) except UnicodeError: tkMessageBox.showerror( "I/O Error", "Cannot save this as '%s' anymore. Saving as UTF-8" \ % self.fileencoding, master = self.text) return BOM_UTF8 + chars.encode("utf-8") # Nothing was declared, and we had not determined an encoding # on loading. Recommend an encoding line. config_encoding = idleConf.GetOption("main", "EditorWindow", "encoding") if config_encoding == 'utf-8': # User has requested that we save files as UTF-8 return BOM_UTF8 + chars.encode("utf-8") ask_user = True try: chars = chars.encode(encoding) enc = encoding if config_encoding == 'locale': ask_user = False except UnicodeError: chars = BOM_UTF8 + chars.encode("utf-8") enc = "utf-8" if not ask_user: return chars dialog = EncodingMessage(self.editwin.top, enc)
class AutoComplete: """ Extension to allow automatic completion of text. Set the autocomplete and try-open-completions key bindings. Options: popupwait - how many millisecs to wait before opening window. onlycontaining - if True, the autocomplete window will first only show names that contain the typed text. If you double press the key for autocomplete, the list of names will grow to include all names. imports - if True, names available for import statements will be completed too. imports-timeout - (proximal) maximum delay until import completion returns. twotabstocomplete - sets if two tabs are required to complete text once window is open. entertocomplete - sets if pressing enter completes a name. dictkeys - if forced open is fired and cursor is just after '[', show window containing the dict keys. showlengths - allows showing of lengths of lists and tuples. """ menudefs = [('edit', [ ("Show Completions", "<<force-open-completions>>"), ])] popupwait = idleConf.GetOption("extensions", "AutoComplete", "popupwait", type="int", default=0) # Flag to show only completions that actually contain typed word. onlycontaining = idleConf.GetOption("extensions", "AutoComplete", "onlycontaining", type="bool", default=False, member_name='onlycontaining') # Flag to auto complete imports. imports = idleConf.GetOption("extensions", "AutoComplete", "imports", type="bool", default=False, member_name='imports') # Flag to complete after two tabs. twotabstocomplete = idleConf.GetOption("extensions", "AutoComplete", "twotabstocomplete", type="bool", default=True, member_name='twotabstocomplete') # Flag to complete when enter is pressed. entertocomplete = idleConf.GetOption("extensions", "AutoComplete", "entertocomplete", type="bool", default=False, member_name='entertocomplete') # Flag to show dictionary keys. dictkeys = idleConf.GetOption("extensions", "AutoComplete", "dictkeys", type="bool", default=False, member_name='dictkeys') # Flag to allow showing length of lists and tuples. showlengths = idleConf.GetOption("extensions", "AutoComplete", "showlengths", type="bool", default=False, member_name='showlengths') 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, self.twotabstocomplete, self.entertocomplete) def _remove_autocomplete_window(self, event=None): if self.autocompletewindow: self.autocompletewindow.hide_window() self.autocompletewindow = None def is_executing(self): return isinstance(self.editwin, PyShell.PyShell) and \ self.editwin.executing 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. """ if self.is_executing(): return 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. """ if self.is_executing(): return 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 # If the window is already open, show the big list of completions. # This means that a double Ctrl-space opens the big list every time, which is nice. if self.autocompletewindow is not None and self.autocompletewindow.autocompletewindow is not None: showbig = True else: showbig = False hp = HyperParser(self.editwin, "insert") curline = self.text.get("insert linestart", "insert") i = j = len(curline) # Check if it's an import statement. # This is seperated from the rest since the pattern check is in ModuleCompletion. imports = self.get_module_completion(curline) if imports: if imports == ([], []): return comp_lists = imports while i and curline[i - 1] in ID_CHARS: i -= 1 comp_start = curline[i:j] elif self.dictkeys and hp.is_in_dict() and ( not mode or mode == COMPLETE_KEYS) and evalfuncs: self._remove_autocomplete_window() mode = COMPLETE_KEYS while i and curline[i - 1] in ID_CHARS + '"' + "'": i -= 1 comp_start = curline[i:j] if curline[i - 1:i] == "[": hp.set_index("insert-%dc" % (len(curline) - (i - 1))) comp_what = hp.get_expression() else: comp_what = "" elif (hp.is_in_string() or hp.is_in_command()) \ and (not mode or mode==COMPLETE_FILES): self._remove_autocomplete_window() mode = COMPLETE_FILES while i and curline[i - 1] in FILENAME_CHARS: i -= 1 comp_start = curline[i:j] j = i while i and curline[i - 1] in FILENAME_CHARS + SEPS: 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: 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 # For everything but imports, call fetch_completions if not imports: 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 # It's nice to be able to see the length of a tuple/list (but not anything more complicated) if comp_lists[0] == SHOWCALLTIP: parenleft = self.text.index('insert-1c') CallTip(self.text).showtip(comp_lists[1], parenleft, parenleft.split('.')[0] + '.end') return if mode == COMPLETE_ATTRIBUTES and not imports: calltips = self.editwin.extensions.get('CallTips') if calltips: args = calltips.arg_names(evalfuncs) if args: args = [a + '=' for a in args] comp_lists = sorted(comp_lists[0] + args), sorted(comp_lists[1] + args) # Check if we want to show only completion containing typed word. if self.onlycontaining: # Small optimization comp_lower = comp_start.lower() # Find such completions. comp_lists = [ name for name in comp_lists[0] if comp_lower in name.lower() ], comp_lists[1] # If none were found, look in big list. if not comp_lists[0]: comp_lists = [ name for name in comp_lists[1] if comp_lower in name.lower() ], comp_lists[1] # If still none were found, just return the big list - which is the default anyway. if not comp_lists[0]: comp_lists = comp_lists[1], comp_lists[1] if showbig: comp_lists = comp_lists[1], [] self.autocompletewindow = self._make_autocomplete_window() return not self.autocompletewindow.show_window( comp_lists, "insert-%dc" % len(comp_start), complete, mode, userWantsWin, onlycontaining=self.onlycontaining) 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: import run bigl = smalll = [] if mode == COMPLETE_ATTRIBUTES: if what == "": namespace = run.World.executive.locals.copy() namespace.update(__main__.__builtins__.__dict__) bigl = eval("dir()", namespace) + keyword.kwlist bigl = sorted(set(bigl)) if "__all__" in bigl: smalll = sorted(set(eval("__all__", namespace))) else: smalll = [s for s in bigl if s[:1] != '_'] else: try: entity = self.get_entity(what) bigl = dir(entity) bigl = sorted(set(bigl)) if "__all__" in bigl: smalll = sorted(set(entity.__all__)) else: smalll = [s for s in bigl if s[:1] != '_'] except: return [], [] elif mode == COMPLETE_FILES: if what == "": what = "." try: from os.path import normcase expandedpath = os.path.expanduser(what) bigl = os.listdir(expandedpath) try: cmp_ = cmp except NameError: cmp_ = lambda x, y: (x > y) - (x < y) bigl = sorted( set(bigl), cmp=lambda x, y: cmp_(normcase(x), normcase(y))) smalll = [s for s in bigl if s[:1] != '.'] except OSError: return [], [] elif mode == COMPLETE_KEYS: entity = None try: entity = self.get_entity(what) keys = set() for key in entity.keys(): try: r = repr(key) if not r.startswith('<'): keys.add(r) except: pass smalll = bigl = sorted(keys) except: # If the entity is a list or tuple let's show the length. # It is so common to go back to the start of the line, write "len(", go to the end, write ")", # evaluate, then remove the "len", and finally do what you actually wanted to do... # In any case, it's configurable. try: if isinstance(entity, list): return SHOWCALLTIP, 'list[0:%d]' % len(entity) elif isinstance(entity, tuple): return SHOWCALLTIP, 'tuple[0:%d]' % len(entity) elif entity.__class__.__name__ == 'ndarray': return SHOWCALLTIP, 'ndarray({}, dtype={})'.format( entity.shape, entity.dtype) except: pass 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__ or import module""" import run namespace = sys.modules.copy() namespace.update(run.World.executive.locals) return eval(name, namespace) @remoteboundmethod def get_module_completion(self, line): """Get module completions for the line""" return ModuleCompletion.module_completion(line)
"""About Dialog for IDLE""" import os import sys from Tkinter import Toplevel, Frame, Button, Label, TkVersion from Tkconstants import LEFT, NSEW, SUNKEN, EW, W, BOTH, TOP, BOTTOM import idlever import textView from stylist import PoorManStyle from configHandler import idleConf TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int') if TTK: from ttk import Frame, Button, Label, Style class AboutDialog(Toplevel): """Modal about dialog for idle """ def __init__(self, parent, title): Toplevel.__init__(self, parent) self.configure(borderwidth=5) self.geometry("+%d+%d" % (parent.winfo_rootx() + 30, parent.winfo_rooty() + 30)) self.bg = "#707070" self.fg = "#ffffff" self.SetupStyles() self.CreateWidgets()
class Squeezer: """ Extension to squeeze long output into a button This file was originally copied for Tal Einat's Squeezer package. Options: max-num-of-lines - output bigger than this will be squeezed. min-num-of-lines - output smaller than this will *not* be squeezed. max-expand - output will never be more than this amount of characters. preview-command-nt(-win) - command to open preview application. squeeze-code - flag for allowing to squeeze code. Configure the key bindings expand-last-squeezed, preview-last-squeezed and squeeze-current-text. """ _MAX_NUM_OF_LINES = idleConf.GetOption("extensions", "Squeezer", "max-num-of-lines", type="int", default=30) _MAX_EXPAND = idleConf.GetOption("extensions", "Squeezer", "max-expand", type="int", default=50000) _PREVIEW_COMMAND = idleConf.GetOption("extensions", "Squeezer", "preview-command-" + {"nt": "win"}.get(os.name, os.name), default="", raw=True) # Flag for whether or not stdin can be squeezing _SQUEEZE_CODE = idleConf.GetOption("extensions", "Squeezer", "squeeze-code", type="bool", default=False, member_name='_SQUEEZE_CODE') _MIN_NUM_OF_LINES = idleConf.GetOption("extensions", "Squeezer", "min-num-of-lines", type="int", default=1, member_name="_MIN_NUM_OF_LINES") menudefs = [( 'edit', [ None, # Separator ("Expand last squeezed text", "<<expand-last-squeezed>>"), ])] if _PREVIEW_COMMAND: menudefs[0][1].append( ("Preview last squeezed text", "<<preview-last-squeezed>>")) def __init__(self, editwin): """ Args: editwin (PyShell): pyshell window. """ self.editwin = editwin self.text = text = editwin.text self.expandingbuttons = [] if isinstance(editwin, PyShell): # If we get a PyShell instance, replace its write method with a # wrapper, which inserts an ExpandingButton instead of a long text. def mywrite(s, tags=(), write=editwin.write): if tags != "stdout": return write(s, tags) else: numoflines = self.count_lines(s) if numoflines < self._MAX_NUM_OF_LINES: return write(s, tags) else: expandingbutton = ExpandingButton(s, tags, numoflines, self, check_links=True) text.mark_gravity("iomark", Tkinter.RIGHT) text.window_create("iomark", window=expandingbutton, padx=3, pady=5) text.see("iomark") text.update() text.mark_gravity("iomark", Tkinter.LEFT) self.expandingbuttons.append(expandingbutton) editwin.write = mywrite # Add squeeze-current-text to the right-click menu text.bind("<<squeeze-current-text>>", self.squeeze_current_text_event) _add_to_rmenu( editwin, [("_Squeeze current text", "<<squeeze-current-text>>")]) def count_lines(self, s): """ Calculate number of lines in given text. Before calculation, the tab width and line length of the text are fetched, so that up-to-date values are used. """ # Tab width is configurable tabwidth = self.editwin.get_tabwidth() text = self.editwin.text # Get the Text widget's size linewidth = text.winfo_width() # Deduct the border and padding linewidth -= 2 * sum( [int(text.cget(opt)) for opt in ('border', 'padx')]) # Get the Text widget's font font = tkFont.Font(text, name=text.cget('font')) # Divide the size of the Text widget by the font's width. # According to Tk8.4 docs, the Text widget's width is set # according to the width of its font's '0' (zero) character, # so we will use this as an approximation. linewidth //= font.measure('0') return _countlines(s, linewidth, tabwidth) def expand_last_squeezed_event(self, event): """Expands bottom most squeezed block""" if self.expandingbuttons: self.expandingbuttons[-1].expand(event) else: self.text.bell() return "break" def preview_last_squeezed_event(self, event): """Opens a defined application to show squeezed text""" if self._PREVIEW_COMMAND and self.expandingbuttons: self.expandingbuttons[-1].preview(event) else: self.text.bell() return "break" def squeeze_last_output_event(self, event): """Squeezes bottom most un-squeezed block""" last_console = self.text.tag_prevrange("console", Tkinter.END) if not last_console: return "break" prev_ranges = [] if self._SQUEEZE_CODE: valid_tags = ("stdout", "stderr", "stdin") else: valid_tags = ("stdout", "stderr") for tag_name in valid_tags: rng = last_console while rng: rng = self.text.tag_prevrange(tag_name, rng[0]) if rng: txt = self.text.get(*rng).strip() if txt and ( tag_name != 'stdin' or '\n' in txt ) and self.count_lines(txt) > self._MIN_NUM_OF_LINES: prev_ranges.append((rng, tag_name)) break if not prev_ranges: return "break" # Get max element dynamically, in case the number of allowed tags to squeeze ever changes. max_rng = prev_ranges[0] for rng in prev_ranges[1:]: if self.text.compare(max_rng[0][0], '<', rng[0][0]): max_rng = rng if not self.squeeze_range(*max_rng): self.text.bell() return "break" def squeeze_current_text_event(self, event): """Squeeze selected block""" insert_tag_names = self.text.tag_names(Tkinter.INSERT) for tag_name in ("stdout", "stderr"): if tag_name in insert_tag_names: break else: # Check if code squeezing is enabled. if self._SQUEEZE_CODE and 'stdin' in insert_tag_names: tag_name = 'stdin' else: # no tag associated with the index self.text.bell() return "break" # find the range to squeeze rng = self.text.tag_prevrange(tag_name, Tkinter.INSERT + "+1c") if not self.squeeze_range(rng, tag_name): self.text.bell() return "break" def squeeze_range(self, rng, tag_name): if not rng or rng[0] == rng[1]: return False start, end = rng # If it's code that we are squeezing then we only squeeze from the second row. I think this is nicer, because # mostly we'll be squeezing function definitions, and this will keep the 'def ...' visible. if tag_name == 'stdin': # It's nice to save the line just before, the "def" line, so it appears in preview and copy # of the ExpandingButton. def_line = self.text.get(start, start + " lineend") start = self.text.index("%s+1l linestart" % start) else: def_line = None old_expandingbutton = self.find_button(end) s = self.text.get(start, end) # if the last char is a newline, remove it from the range if s and s[-1] == '\n': end = self.text.index("%s-1c" % end) s = s[:-1] # This will preserve all tags, such as for colors and links rangetags = tags_in_range(self.text, start, end) # delete the text _get_base_text(self.editwin).delete(start, end) if old_expandingbutton is not None and \ old_expandingbutton.tags == tag_name: old_expandingbutton.expand_back(s) return True # prepare an ExpandingButton numoflines = self.count_lines(s) expandingbutton = ExpandingButton(s, tag_name, numoflines, self, def_line=def_line, rangetags=rangetags) # insert the ExpandingButton to the Text self.text.window_create(start, window=expandingbutton, padx=3, pady=5) # insert the ExpandingButton to the list of ExpandingButtons i = len(self.expandingbuttons) while i > 0 and self.text.compare(self.expandingbuttons[i - 1], ">", expandingbutton): i -= 1 self.expandingbuttons.insert(i, expandingbutton) return True def find_button(self, pos): for btn in self.expandingbuttons: try: if self.text.compare(pos, "==", btn): return btn except Tkinter.TclError: pass return None
""" An auto-completion window for IDLE, used by the AutoComplete extension """ from Tkinter import Toplevel, Scrollbar, Listbox from Tkinter import TclError from Tkinter import END, VERTICAL, RIGHT, LEFT, BOTH, Y from idlesporklib.MultiCall import MC_SHIFT from idlesporklib.AutoComplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES from configHandler import idleConf theme = idleConf.GetOption('main', 'Theme', 'name') HIDE_VIRTUAL_EVENT_NAME = "<<autocompletewindow-hide>>" HIDE_SEQUENCES = ("<FocusOut>", "<ButtonPress>") KEYPRESS_VIRTUAL_EVENT_NAME = "<<autocompletewindow-keypress>>" # We need to bind event beyond <Key> so that the function will be called # before the default specific IDLE function KEYPRESS_SEQUENCES = ("<Key>", "<Key-BackSpace>", "<Key-Return>", "<Key-Tab>", "<Key-Up>", "<Key-Down>", "<Key-Home>", "<Key-End>", "<Key-Prior>", "<Key-Next>") KEYRELEASE_VIRTUAL_EVENT_NAME = "<<autocompletewindow-keyrelease>>" KEYRELEASE_SEQUENCE = "<KeyRelease>" LISTUPDATE_SEQUENCE = "<B1-ButtonRelease>" WINCONFIG_SEQUENCE = "<Configure>" DOUBLECLICK_SEQUENCE = "<B1-Double-ButtonRelease>" class AutoCompleteWindow: # Save global instance to make sure there is always at most one window. instance = None