diff options
-rw-r--r-- | Tools/idle/AutoIndent.py | 551 | ||||
-rw-r--r-- | Tools/idle/FrameViewer.py | 38 | ||||
-rw-r--r-- | Tools/idle/IdleConf.py | 113 | ||||
-rw-r--r-- | Tools/idle/MultiScrolledLists.py | 137 | ||||
-rw-r--r-- | Tools/idle/OldStackViewer.py | 275 | ||||
-rw-r--r-- | Tools/idle/RemoteInterp.py | 341 | ||||
-rw-r--r-- | Tools/idle/SearchBinding.py | 97 | ||||
-rw-r--r-- | Tools/idle/Separator.py | 92 | ||||
-rw-r--r-- | Tools/idle/config-mac.txt | 3 | ||||
-rw-r--r-- | Tools/idle/config-unix.txt | 4 | ||||
-rw-r--r-- | Tools/idle/config-win.txt | 4 | ||||
-rw-r--r-- | Tools/idle/config.txt | 64 | ||||
-rw-r--r-- | Tools/idle/eventparse.py | 89 | ||||
-rw-r--r-- | Tools/idle/keydefs.py | 57 |
14 files changed, 1865 insertions, 0 deletions
diff --git a/Tools/idle/AutoIndent.py b/Tools/idle/AutoIndent.py new file mode 100644 index 0000000..7bc195b --- /dev/null +++ b/Tools/idle/AutoIndent.py @@ -0,0 +1,551 @@ +#from Tkinter import TclError +#import tkMessageBox +#import tkSimpleDialog + +###$ event <<newline-and-indent>> +###$ win <Key-Return> +###$ win <KP_Enter> +###$ unix <Key-Return> +###$ unix <KP_Enter> + +###$ event <<indent-region>> +###$ win <Control-bracketright> +###$ unix <Alt-bracketright> +###$ unix <Control-bracketright> + +###$ event <<dedent-region>> +###$ win <Control-bracketleft> +###$ unix <Alt-bracketleft> +###$ unix <Control-bracketleft> + +###$ event <<comment-region>> +###$ win <Alt-Key-3> +###$ unix <Alt-Key-3> + +###$ event <<uncomment-region>> +###$ win <Alt-Key-4> +###$ unix <Alt-Key-4> + +###$ event <<tabify-region>> +###$ win <Alt-Key-5> +###$ unix <Alt-Key-5> + +###$ event <<untabify-region>> +###$ win <Alt-Key-6> +###$ unix <Alt-Key-6> + +import PyParse + +class AutoIndent: + + menudefs = [ + ('edit', [ + None, + ('_Indent region', '<<indent-region>>'), + ('_Dedent region', '<<dedent-region>>'), + ('Comment _out region', '<<comment-region>>'), + ('U_ncomment region', '<<uncomment-region>>'), + ('Tabify region', '<<tabify-region>>'), + ('Untabify region', '<<untabify-region>>'), + ('Toggle tabs', '<<toggle-tabs>>'), + ('New indent width', '<<change-indentwidth>>'), + ]), + ] + + keydefs = { + '<<smart-backspace>>': ['<Key-BackSpace>'], + '<<newline-and-indent>>': ['<Key-Return>', '<KP_Enter>'], + '<<smart-indent>>': ['<Key-Tab>'] + } + + windows_keydefs = { + '<<indent-region>>': ['<Control-bracketright>'], + '<<dedent-region>>': ['<Control-bracketleft>'], + '<<comment-region>>': ['<Alt-Key-3>'], + '<<uncomment-region>>': ['<Alt-Key-4>'], + '<<tabify-region>>': ['<Alt-Key-5>'], + '<<untabify-region>>': ['<Alt-Key-6>'], + '<<toggle-tabs>>': ['<Alt-Key-t>'], + '<<change-indentwidth>>': ['<Alt-Key-u>'], + } + + unix_keydefs = { + '<<indent-region>>': ['<Alt-bracketright>', + '<Meta-bracketright>', + '<Control-bracketright>'], + '<<dedent-region>>': ['<Alt-bracketleft>', + '<Meta-bracketleft>', + '<Control-bracketleft>'], + '<<comment-region>>': ['<Alt-Key-3>', '<Meta-Key-3>'], + '<<uncomment-region>>': ['<Alt-Key-4>', '<Meta-Key-4>'], + '<<tabify-region>>': ['<Alt-Key-5>', '<Meta-Key-5>'], + '<<untabify-region>>': ['<Alt-Key-6>', '<Meta-Key-6>'], + '<<toggle-tabs>>': ['<Alt-Key-t>'], + '<<change-indentwidth>>': ['<Alt-Key-u>'], + } + + # usetabs true -> literal tab characters are used by indent and + # dedent cmds, possibly mixed with spaces if + # indentwidth is not a multiple of tabwidth + # false -> tab characters are converted to spaces by indent + # and dedent cmds, and ditto TAB keystrokes + # indentwidth is the number of characters per logical indent level. + # 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. + usetabs = 1 + indentwidth = 4 + tabwidth = 8 # for IDLE use, must remain 8 until Tk is fixed + + # If context_use_ps1 is true, parsing searches back for a ps1 line; + # else searches for a popular (if, def, ...) Python stmt. + context_use_ps1 = 0 + + # 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. + num_context_lines = 50, 500, 5000000 + + def __init__(self, editwin): + self.editwin = editwin + self.text = editwin.text + + def config(self, **options): + for key, value in options.items(): + if key == 'usetabs': + self.usetabs = value + elif key == 'indentwidth': + self.indentwidth = value + elif key == 'tabwidth': + self.tabwidth = value + elif key == 'context_use_ps1': + self.context_use_ps1 = value + else: + raise KeyError, "bad option name: %s" % `key` + + # If ispythonsource and guess are true, guess a good value for + # indentwidth based on file content (if possible), and if + # indentwidth != tabwidth set usetabs false. + # In any case, adjust the Text widget's view of what a tab + # character means. + + def set_indentation_params(self, ispythonsource, guess=1): + if guess and ispythonsource: + i = self.guess_indent() + if 2 <= i <= 8: + self.indentwidth = i + if self.indentwidth != self.tabwidth: + self.usetabs = 0 + + self.editwin.set_tabwidth(self.tabwidth) + + def smart_backspace_event(self, event): + text = self.text + first, last = self.editwin.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 + ncharsdeleted = 0 + while 1: + 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 & return + # indent one level + text = self.text + first, last = self.editwin.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: + 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.editwin.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; just inject + # an empty line at the start + text.insert("insert linestart", '\n') + return "break" + indent = line[:i] + # strip whitespace before insert point + i = 0 + while line and line[-1] in " \t": + 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) + for context in self.num_context_lines: + startat = max(lno - context, 1) + startatindex = `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) + c = y.get_continuation_type() + if c != PyParse.C_NONE: + # The current stmt hasn't ended yet. + if c == PyParse.C_STRING: + # inside a string; 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 " + `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() + + auto_indent = newline_and_indent_event + + # 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.editwin.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() + 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() + 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.editwin.askyesno( + "Toggle tabs", + "Turn tabs " + ("on", "off")[self.usetabs] + "?", + parent=self.text): + self.usetabs = not self.usetabs + return "break" + + # XXX this isn't bound to anything -- see class 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.editwin.askinteger( + "Indent width", + "New indent width (1-16)", + parent=self.text, + initialvalue=self.indentwidth, + minvalue=1, + maxvalue=16) + if new and new != self.indentwidth: + self.indentwidth = new + return "break" + + def get_region(self): + text = self.text + first, last = self.editwin.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.editwin.askinteger( + "Tab width", + "Spaces per tab?", + parent=self.text, + initialvalue=self.tabwidth, + minvalue=1, + maxvalue=16) or self.tabwidth + + # 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: + + # .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 = `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: + _tokenize.tokenize(self.readline, self.tokeneater) + except _tokenize.TokenError: + # since we cut off the tokenizer early, we can trigger + # spurious errors + pass + finally: + _tokenize.tabsize = save_tabsize + return self.blkopenline, self.indentedline diff --git a/Tools/idle/FrameViewer.py b/Tools/idle/FrameViewer.py new file mode 100644 index 0000000..2ce0935 --- /dev/null +++ b/Tools/idle/FrameViewer.py @@ -0,0 +1,38 @@ +from repr import Repr +from Tkinter import * + +class FrameViewer: + + def __init__(self, root, frame): + self.root = root + self.frame = frame + self.top = Toplevel(self.root) + self.repr = Repr() + self.repr.maxstring = 60 + self.load_variables() + + def load_variables(self): + row = 0 + if self.frame.f_locals is not self.frame.f_globals: + l = Label(self.top, text="Local Variables", + borderwidth=2, relief="raised") + l.grid(row=row, column=0, columnspan=2, sticky="ew") + row = self.load_names(self.frame.f_locals, row+1) + l = Label(self.top, text="Global Variables", + borderwidth=2, relief="raised") + l.grid(row=row, column=0, columnspan=2, sticky="ew") + row = self.load_names(self.frame.f_globals, row+1) + + def load_names(self, dict, row): + names = dict.keys() + names.sort() + for name in names: + value = dict[name] + svalue = self.repr.repr(value) + l = Label(self.top, text=name) + l.grid(row=row, column=0, sticky="w") + l = Entry(self.top, width=60, borderwidth=0) + l.insert(0, svalue) + l.grid(row=row, column=1, sticky="w") + row = row+1 + return row diff --git a/Tools/idle/IdleConf.py b/Tools/idle/IdleConf.py new file mode 100644 index 0000000..8eaa8e0 --- /dev/null +++ b/Tools/idle/IdleConf.py @@ -0,0 +1,113 @@ +"""Provides access to configuration information""" + +import os +import sys +from ConfigParser import ConfigParser, NoOptionError, NoSectionError + +class IdleConfParser(ConfigParser): + + # these conf sections do not define extensions! + builtin_sections = {} + for section in ('EditorWindow', 'Colors'): + builtin_sections[section] = section + + def getcolor(self, sec, name): + """Return a dictionary with foreground and background colors + + The return value is appropriate for passing to Tkinter in, e.g., + a tag_config call. + """ + fore = self.getdef(sec, name + "-foreground") + back = self.getdef(sec, name + "-background") + return {"foreground": fore, + "background": back} + + def getdef(self, sec, options, raw=0, vars=None, default=None): + """Get an option value for given section or return default""" + try: + return self.get(sec, options, raw, vars) + except (NoSectionError, NoOptionError): + return default + + def getsection(self, section): + """Return a SectionConfigParser object""" + return SectionConfigParser(section, self) + + def getextensions(self): + exts = [] + for sec in self.sections(): + if self.builtin_sections.has_key(sec): + continue + # enable is a bool, but it may not be defined + if self.getdef(sec, 'enable') != '0': + exts.append(sec) + return exts + + def reload(self): + global idleconf + idleconf = IdleConfParser() + load(_dir) # _dir is a global holding the last directory loaded + +class SectionConfigParser: + """A ConfigParser object specialized for one section + + This class has all the get methods that a regular ConfigParser does, + but without requiring a section argument. + """ + def __init__(self, section, config): + self.section = section + self.config = config + + def options(self): + return self.config.options(self.section) + + def get(self, options, raw=0, vars=None): + return self.config.get(self.section, options, raw, vars) + + def getdef(self, options, raw=0, vars=None, default=None): + return self.config.getdef(self.section, options, raw, vars, default) + + def getint(self, option): + return self.config.getint(self.section, option) + + def getfloat(self, option): + return self.config.getint(self.section, option) + + def getboolean(self, option): + return self.config.getint(self.section, option) + + def getcolor(self, option): + return self.config.getcolor(self.section, option) + +def load(dir): + """Load IDLE configuration files based on IDLE install in dir + + Attempts to load two config files: + dir/config.txt + dir/config-[win/mac/unix].txt + dir/config-%(sys.platform)s.txt + ~/.idle + """ + global _dir + _dir = dir + + if sys.platform[:3] == 'win': + genplatfile = os.path.join(dir, "config-win.txt") + # XXX don't know what the platform string is on a Mac + elif sys.platform[:3] == 'mac': + genplatfile = os.path.join(dir, "config-mac.txt") + else: + genplatfile = os.path.join(dir, "config-unix.txt") + + platfile = os.path.join(dir, "config-%s.txt" % sys.platform) + + try: + homedir = os.environ['HOME'] + except KeyError: + homedir = os.getcwd() + + idleconf.read((os.path.join(dir, "config.txt"), genplatfile, platfile, + os.path.join(homedir, ".idle"))) + +idleconf = IdleConfParser() +load(os.path.dirname(__file__)) diff --git a/Tools/idle/MultiScrolledLists.py b/Tools/idle/MultiScrolledLists.py new file mode 100644 index 0000000..6398b86 --- /dev/null +++ b/Tools/idle/MultiScrolledLists.py @@ -0,0 +1,137 @@ +# One or more ScrolledLists with HSeparators between them. +# There is a hierarchical relationship between them: +# the right list displays the substructure of the selected item +# in the left list. + +from Tkinter import * +from WindowList import ListedToplevel +from Separator import HSeparator +from ScrolledList import ScrolledList + +class MultiScrolledLists: + + def __init__(self, root, nlists=2): + assert nlists >= 1 + self.root = root + self.nlists = nlists + self.path = [] + # create top + self.top = top = ListedToplevel(root) + top.protocol("WM_DELETE_WINDOW", self.close) + top.bind("<Escape>", self.close) + self.settitle() + # create frames and separators in between + self.frames = [] + self.separators = [] + last = top + for i in range(nlists-1): + sepa = HSeparator(last) + self.separators.append(sepa) + frame, last = sepa.parts() + self.frames.append(frame) + self.frames.append(last) + # create labels and lists + self.labels = [] + self.lists = [] + for i in range(nlists): + frame = self.frames[i] + label = Label(frame, text=self.subtitle(i), + relief="groove", borderwidth=2) + label.pack(fill="x") + self.labels.append(label) + list = ScrolledList(frame, width=self.width(i), + height=self.height(i)) + self.lists.append(list) + list.on_select = \ + lambda index, i=i, self=self: self.on_select(index, i) + list.on_double = \ + lambda index, i=i, self=self: self.on_double(index, i) + # fill leftmost list (rest get filled on demand) + self.fill(0) + # XXX one after_idle isn't enough; two are... + top.after_idle(self.call_pack_propagate_1) + + def call_pack_propagate_1(self): + self.top.after_idle(self.call_pack_propagate) + + def call_pack_propagate(self): + for frame in self.frames: + frame.pack_propagate(0) + + def close(self, event=None): + self.top.destroy() + + def settitle(self): + short = self.shorttitle() + long = self.longtitle() + if short and long: + title = short + " - " + long + elif short: + title = short + elif long: + title = long + else: + title = "Untitled" + icon = short or long or title + self.top.wm_title(title) + self.top.wm_iconname(icon) + + def longtitle(self): + # override this + return "Multi Scrolled Lists" + + def shorttitle(self): + # override this + return None + + def width(self, i): + # override this + return 20 + + def height(self, i): + # override this + return 10 + + def subtitle(self, i): + # override this + return "Column %d" % i + + def fill(self, i): + for k in range(i, self.nlists): + self.lists[k].clear() + self.labels[k].configure(text=self.subtitle(k)) + list = self.lists[i] + l = self.items(i) + for s in l: + list.append(s) + + def on_select(self, index, i): + item = self.lists[i].get(index) + del self.path[i:] + self.path.append(item) + if i+1 < self.nlists: + self.fill(i+1) + + def items(self, i): + # override this + l = [] + for k in range(10): + s = str(k) + if i > 0: + s = self.path[i-1] + "." + s + l.append(s) + return l + + def on_double(self, index, i): + pass + + +def main(): + root = Tk() + quit = Button(root, text="Exit", command=root.destroy) + quit.pack() + MultiScrolledLists(root, 4) + root.mainloop() + +if __name__ == "__main__": + main() diff --git a/Tools/idle/OldStackViewer.py b/Tools/idle/OldStackViewer.py new file mode 100644 index 0000000..4f295e8 --- /dev/null +++ b/Tools/idle/OldStackViewer.py @@ -0,0 +1,275 @@ +import sys +import os +from Tkinter import * +import linecache +from repr import Repr +from WindowList import ListedToplevel + +from ScrolledList import ScrolledList + + +class StackBrowser: + + def __init__(self, root, flist, stack=None): + self.top = top = ListedToplevel(root) + top.protocol("WM_DELETE_WINDOW", self.close) + top.bind("<Key-Escape>", self.close) + top.wm_title("Stack viewer") + top.wm_iconname("Stack") + # Create help label + self.helplabel = Label(top, + text="Click once to view variables; twice for source", + borderwidth=2, relief="groove") + self.helplabel.pack(fill="x") + # + self.sv = StackViewer(top, flist, self) + if stack is None: + stack = get_stack() + self.sv.load_stack(stack) + + def close(self, event=None): + self.top.destroy() + + localsframe = None + localsviewer = None + localsdict = None + globalsframe = None + globalsviewer = None + globalsdict = None + curframe = None + + def show_frame(self, (frame, lineno)): + if frame is self.curframe: + return + self.curframe = None + if frame.f_globals is not self.globalsdict: + self.show_globals(frame) + self.show_locals(frame) + self.curframe = frame + + def show_globals(self, frame): + title = "Global Variables" + if frame.f_globals.has_key("__name__"): + try: + name = str(frame.f_globals["__name__"]) + "" + except: + name = "" + if name: + title = title + " in module " + name + self.globalsdict = None + if self.globalsviewer: + self.globalsviewer.close() + self.globalsviewer = None + if not self.globalsframe: + self.globalsframe = Frame(self.top) + self.globalsdict = frame.f_globals + self.globalsviewer = NamespaceViewer( + self.globalsframe, + title, + self.globalsdict) + self.globalsframe.pack(fill="both", side="bottom") + + def show_locals(self, frame): + self.localsdict = None + if self.localsviewer: + self.localsviewer.close() + self.localsviewer = None + if frame.f_locals is not frame.f_globals: + title = "Local Variables" + code = frame.f_code + funcname = code.co_name + if funcname not in ("?", "", None): + title = title + " in " + funcname + if not self.localsframe: + self.localsframe = Frame(self.top) + self.localsdict = frame.f_locals + self.localsviewer = NamespaceViewer( + self.localsframe, + title, + self.localsdict) + self.localsframe.pack(fill="both", side="top") + else: + if self.localsframe: + self.localsframe.forget() + + +class StackViewer(ScrolledList): + + def __init__(self, master, flist, browser): + ScrolledList.__init__(self, master, width=80) + self.flist = flist + self.browser = browser + self.stack = [] + + def load_stack(self, stack, index=None): + self.stack = stack + self.clear() +## if len(stack) > 10: +## l["height"] = 10 +## self.topframe.pack(expand=1) +## else: +## l["height"] = len(stack) +## self.topframe.pack(expand=0) + for i in range(len(stack)): + frame, lineno = stack[i] + try: + modname = frame.f_globals["__name__"] + except: + modname = "?" + code = frame.f_code + filename = code.co_filename + funcname = code.co_name + sourceline = linecache.getline(filename, lineno) + sourceline = sourceline.strip() + if funcname in ("?", "", None): + item = "%s, line %d: %s" % (modname, lineno, sourceline) + else: + item = "%s.%s(), line %d: %s" % (modname, funcname, + lineno, sourceline) + if i == index: + item = "> " + item + self.append(item) + if index is not None: + self.select(index) + + def popup_event(self, event): + if self.stack: + return ScrolledList.popup_event(self, event) + + def fill_menu(self): + menu = self.menu + menu.add_command(label="Go to source line", + command=self.goto_source_line) + menu.add_command(label="Show stack frame", + command=self.show_stack_frame) + + def on_select(self, index): + if 0 <= index < len(self.stack): + self.browser.show_frame(self.stack[index]) + + def on_double(self, index): + self.show_source(index) + + def goto_source_line(self): + index = self.listbox.index("active") + self.show_source(index) + + def show_stack_frame(self): + index = self.listbox.index("active") + if 0 <= index < len(self.stack): + self.browser.show_frame(self.stack[index]) + + def show_source(self, index): + if not (0 <= index < len(self.stack)): + return + frame, lineno = self.stack[index] + code = frame.f_code + filename = code.co_filename + if os.path.isfile(filename): + edit = self.flist.open(filename) + if edit: + edit.gotoline(lineno) + + +def get_stack(t=None, f=None): + if t is None: + t = sys.last_traceback + stack = [] + if t and t.tb_frame is f: + t = t.tb_next + while f is not None: + stack.append((f, f.f_lineno)) + if f is self.botframe: + break + f = f.f_back + stack.reverse() + while t is not None: + stack.append((t.tb_frame, t.tb_lineno)) + t = t.tb_next + return stack + + +def getexception(type=None, value=None): + if type is None: + type = sys.last_type + value = sys.last_value + if hasattr(type, "__name__"): + type = type.__name__ + s = str(type) + if value is not None: + s = s + ": " + str(value) + return s + + +class NamespaceViewer: + + def __init__(self, master, title, dict=None): + width = 0 + height = 40 + if dict: + height = 20*len(dict) # XXX 20 == observed height of Entry widget + self.master = master + self.title = title + self.repr = Repr() + self.repr.maxstring = 60 + self.repr.maxother = 60 + self.frame = frame = Frame(master) + self.frame.pack(expand=1, fill="both") + self.label = Label(frame, text=title, borderwidth=2, relief="groove") + self.label.pack(fill="x") + self.vbar = vbar = Scrollbar(frame, name="vbar") + vbar.pack(side="right", fill="y") + self.canvas = canvas = Canvas(frame, + height=min(300, max(40, height)), + scrollregion=(0, 0, width, height)) + canvas.pack(side="left", fill="both", expand=1) + vbar["command"] = canvas.yview + canvas["yscrollcommand"] = vbar.set + self.subframe = subframe = Frame(canvas) + self.sfid = canvas.create_window(0, 0, window=subframe, anchor="nw") + self.load_dict(dict) + + dict = -1 + + def load_dict(self, dict, force=0): + if dict is self.dict and not force: + return + subframe = self.subframe + frame = self.frame + for c in subframe.children.values(): + c.destroy() + self.dict = None + if not dict: + l = Label(subframe, text="None") + l.grid(row=0, column=0) + else: + names = dict.keys() + names.sort() + row = 0 + for name in names: + value = dict[name] + svalue = self.repr.repr(value) # repr(value) + l = Label(subframe, text=name) + l.grid(row=row, column=0, sticky="nw") + ## l = Label(subframe, text=svalue, justify="l", wraplength=300) + l = Entry(subframe, width=0, borderwidth=0) + l.insert(0, svalue) + ## l["state"] = "disabled" + l.grid(row=row, column=1, sticky="nw") + row = row+1 + self.dict = dict + # XXX Could we use a <Configure> callback for the following? + subframe.update_idletasks() # Alas! + width = subframe.winfo_reqwidth() + height = subframe.winfo_reqheight() + canvas = self.canvas + self.canvas["scrollregion"] = (0, 0, width, height) + if height > 300: + canvas["height"] = 300 + frame.pack(expand=1) + else: + canvas["height"] = height + frame.pack(expand=0) + + def close(self): + self.frame.destroy() diff --git a/Tools/idle/RemoteInterp.py b/Tools/idle/RemoteInterp.py new file mode 100644 index 0000000..e6f7671 --- /dev/null +++ b/Tools/idle/RemoteInterp.py @@ -0,0 +1,341 @@ +import select +import socket +import struct +import sys +import types + +VERBOSE = None + +class SocketProtocol: + """A simple protocol for sending strings across a socket""" + BUF_SIZE = 8192 + + def __init__(self, sock): + self.sock = sock + self._buffer = '' + self._closed = 0 + + def close(self): + self._closed = 1 + self.sock.close() + + def send(self, buf): + """Encode buf and write it on the socket""" + if VERBOSE: + VERBOSE.write('send %d:%s\n' % (len(buf), `buf`)) + self.sock.send('%d:%s' % (len(buf), buf)) + + def receive(self, timeout=0): + """Get next complete string from socket or return None + + Raise EOFError on EOF + """ + buf = self._read_from_buffer() + if buf is not None: + return buf + recvbuf = self._read_from_socket(timeout) + if recvbuf is None: + return None + if recvbuf == '' and self._buffer == '': + raise EOFError + if VERBOSE: + VERBOSE.write('recv %s\n' % `recvbuf`) + self._buffer = self._buffer + recvbuf + r = self._read_from_buffer() + return r + + def _read_from_socket(self, timeout): + """Does not block""" + if self._closed: + return '' + if timeout is not None: + r, w, x = select.select([self.sock], [], [], timeout) + if timeout is None or r: + return self.sock.recv(self.BUF_SIZE) + else: + return None + + def _read_from_buffer(self): + buf = self._buffer + i = buf.find(':') + if i == -1: + return None + buflen = int(buf[:i]) + enclen = i + 1 + buflen + if len(buf) >= enclen: + s = buf[i+1:enclen] + self._buffer = buf[enclen:] + return s + else: + self._buffer = buf + return None + +# helpers for registerHandler method below + +def get_methods(obj): + methods = [] + for name in dir(obj): + attr = getattr(obj, name) + if callable(attr): + methods.append(name) + if type(obj) == types.InstanceType: + methods = methods + get_methods(obj.__class__) + if type(obj) == types.ClassType: + for super in obj.__bases__: + methods = methods + get_methods(super) + return methods + +class CommandProtocol: + def __init__(self, sockp): + self.sockp = sockp + self.seqno = 0 + self.handlers = {} + + def close(self): + self.sockp.close() + self.handlers.clear() + + def registerHandler(self, handler): + """A Handler is an object with handle_XXX methods""" + for methname in get_methods(handler): + if methname[:7] == "handle_": + name = methname[7:] + self.handlers[name] = getattr(handler, methname) + + def send(self, cmd, arg='', seqno=None): + if arg: + msg = "%s %s" % (cmd, arg) + else: + msg = cmd + if seqno is None: + seqno = self.get_seqno() + msgbuf = self.encode_seqno(seqno) + msg + self.sockp.send(msgbuf) + if cmd == "reply": + return + reply = self.sockp.receive(timeout=None) + r_cmd, r_arg, r_seqno = self._decode_msg(reply) + assert r_seqno == seqno and r_cmd == "reply", "bad reply" + return r_arg + + def _decode_msg(self, msg): + seqno = self.decode_seqno(msg[:self.SEQNO_ENC_LEN]) + msg = msg[self.SEQNO_ENC_LEN:] + parts = msg.split(" ", 2) + if len(parts) == 1: + cmd = msg + arg = '' + else: + cmd = parts[0] + arg = parts[1] + return cmd, arg, seqno + + def dispatch(self): + msg = self.sockp.receive() + if msg is None: + return + cmd, arg, seqno = self._decode_msg(msg) + self._current_reply = seqno + h = self.handlers.get(cmd, self.default_handler) + try: + r = h(arg) + except TypeError, msg: + raise TypeError, "handle_%s: %s" % (cmd, msg) + if self._current_reply is None: + if r is not None: + sys.stderr.write("ignoring %s return value type %s\n" % \ + (cmd, type(r).__name__)) + return + if r is None: + r = '' + if type(r) != types.StringType: + raise ValueError, "invalid return type for %s" % cmd + self.send("reply", r, seqno=seqno) + + def reply(self, arg=''): + """Send a reply immediately + + otherwise reply will be sent when handler returns + """ + self.send("reply", arg, self._current_reply) + self._current_reply = None + + def default_handler(self, arg): + sys.stderr.write("WARNING: unhandled message %s\n" % arg) + return '' + + SEQNO_ENC_LEN = 4 + + def get_seqno(self): + seqno = self.seqno + self.seqno = seqno + 1 + return seqno + + def encode_seqno(self, seqno): + return struct.pack("I", seqno) + + def decode_seqno(self, buf): + return struct.unpack("I", buf)[0] + + +class StdioRedirector: + """Redirect sys.std{in,out,err} to a set of file-like objects""" + + def __init__(self, stdin, stdout, stderr): + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + + def redirect(self): + self.save() + sys.stdin = self.stdin + sys.stdout = self.stdout + sys.stderr = self.stderr + + def save(self): + self._stdin = sys.stdin + self._stdout = sys.stdout + self._stderr = sys.stderr + + def restore(self): + sys.stdin = self._stdin + sys.stdout = self._stdout + sys.stderr = self._stderr + +class IOWrapper: + """Send output from a file-like object across a SocketProtocol + + XXX Should this be more tightly integrated with the CommandProtocol? + """ + + def __init__(self, name, cmdp): + self.name = name + self.cmdp = cmdp + self.buffer = [] + +class InputWrapper(IOWrapper): + def write(self, buf): + # XXX what should this do on Windows? + raise IOError, (9, '[Errno 9] Bad file descriptor') + + def read(self, arg=None): + if arg is not None: + if arg <= 0: + return '' + else: + arg = 0 + return self.cmdp.send(self.name, "read,%s" % arg) + + def readline(self): + return self.cmdp.send(self.name, "readline") + +class OutputWrapper(IOWrapper): + def write(self, buf): + self.cmdp.send(self.name, buf) + + def read(self, arg=None): + return '' + +class RemoteInterp: + def __init__(self, sock): + self._sock = SocketProtocol(sock) + self._cmd = CommandProtocol(self._sock) + self._cmd.registerHandler(self) + + def run(self): + try: + while 1: + self._cmd.dispatch() + except EOFError: + pass + + def handle_execfile(self, arg): + self._cmd.reply() + io = StdioRedirector(InputWrapper("stdin", self._cmd), + OutputWrapper("stdout", self._cmd), + OutputWrapper("stderr", self._cmd)) + io.redirect() + execfile(arg, {'__name__':'__main__'}) + io.restore() + self._cmd.send("terminated") + + def handle_quit(self, arg): + self._cmd.reply() + self._cmd.close() + +def startRemoteInterp(id): + import os + # UNIX domain sockets are simpler for starters + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.bind("/var/tmp/ri.%s" % id) + try: + sock.listen(1) + cli, addr = sock.accept() + rinterp = RemoteInterp(cli) + rinterp.run() + finally: + os.unlink("/var/tmp/ri.%s" % id) + +class RIClient: + """Client of the remote interpreter""" + def __init__(self, sock): + self._sock = SocketProtocol(sock) + self._cmd = CommandProtocol(self._sock) + self._cmd.registerHandler(self) + + def execfile(self, file): + self._cmd.send("execfile", file) + + def run(self): + try: + while 1: + self._cmd.dispatch() + except EOFError: + pass + + def handle_stdout(self, buf): + sys.stdout.write(buf) +## sys.stdout.flush() + + def handle_stderr(self, buf): + sys.stderr.write(buf) + + def handle_stdin(self, arg): + if arg == "readline": + return sys.stdin.readline() + i = arg.find(",") + 1 + bytes = int(arg[i:]) + if bytes == 0: + return sys.stdin.read() + else: + return sys.stdin.read(bytes) + + def handle_terminated(self, arg): + self._cmd.reply() + self._cmd.send("quit") + self._cmd.close() + +def riExec(id, file): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect("/var/tmp/ri.%s" % id) + cli = RIClient(sock) + cli.execfile(file) + cli.run() + +if __name__ == "__main__": + import getopt + + SERVER = 1 + opts, args = getopt.getopt(sys.argv[1:], 'cv') + for o, v in opts: + if o == '-c': + SERVER = 0 + elif o == '-v': + VERBOSE = sys.stderr + id = args[0] + + if SERVER: + startRemoteInterp(id) + else: + file = args[1] + riExec(id, file) diff --git a/Tools/idle/SearchBinding.py b/Tools/idle/SearchBinding.py new file mode 100644 index 0000000..5943e3b --- /dev/null +++ b/Tools/idle/SearchBinding.py @@ -0,0 +1,97 @@ +import tkSimpleDialog + +###$ event <<find>> +###$ win <Control-f> +###$ unix <Control-u><Control-u><Control-s> + +###$ event <<find-again>> +###$ win <Control-g> +###$ win <F3> +###$ unix <Control-u><Control-s> + +###$ event <<find-selection>> +###$ win <Control-F3> +###$ unix <Control-s> + +###$ event <<find-in-files>> +###$ win <Alt-F3> + +###$ event <<replace>> +###$ win <Control-h> + +###$ event <<goto-line>> +###$ win <Alt-g> +###$ unix <Alt-g> + +class SearchBinding: + + windows_keydefs = { + '<<find-again>>': ['<Control-g>', '<F3>'], + '<<find-in-files>>': ['<Alt-F3>'], + '<<find-selection>>': ['<Control-F3>'], + '<<find>>': ['<Control-f>'], + '<<replace>>': ['<Control-h>'], + '<<goto-line>>': ['<Alt-g>'], + } + + unix_keydefs = { + '<<find-again>>': ['<Control-u><Control-s>'], + '<<find-in-files>>': ['<Alt-s>', '<Meta-s>'], + '<<find-selection>>': ['<Control-s>'], + '<<find>>': ['<Control-u><Control-u><Control-s>'], + '<<replace>>': ['<Control-r>'], + '<<goto-line>>': ['<Alt-g>', '<Meta-g>'], + } + + menudefs = [ + ('edit', [ + None, + ('_Find...', '<<find>>'), + ('Find a_gain', '<<find-again>>'), + ('Find _selection', '<<find-selection>>'), + ('Find in Files...', '<<find-in-files>>'), + ('R_eplace...', '<<replace>>'), + ('Go to _line', '<<goto-line>>'), + ]), + ] + + def __init__(self, editwin): + self.editwin = editwin + + def find_event(self, event): + import SearchDialog + SearchDialog.find(self.editwin.text) + return "break" + + def find_again_event(self, event): + import SearchDialog + SearchDialog.find_again(self.editwin.text) + return "break" + + def find_selection_event(self, event): + import SearchDialog + SearchDialog.find_selection(self.editwin.text) + return "break" + + def find_in_files_event(self, event): + import GrepDialog + GrepDialog.grep(self.editwin.text, self.editwin.io, self.editwin.flist) + return "break" + + def replace_event(self, event): + import ReplaceDialog + ReplaceDialog.replace(self.editwin.text) + return "break" + + def goto_line_event(self, event): + text = self.editwin.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") diff --git a/Tools/idle/Separator.py b/Tools/idle/Separator.py new file mode 100644 index 0000000..7145559 --- /dev/null +++ b/Tools/idle/Separator.py @@ -0,0 +1,92 @@ +from Tkinter import * + +class Separator: + + def __init__(self, master, orient, min=10, thickness=5, bg=None): + self.min = max(1, min) + self.thickness = max(1, thickness) + if orient in ("h", "horizontal"): + self.side = "left" + self.dim = "width" + self.dir = "x" + self.cursor = "sb_h_double_arrow" + elif orient in ("v", "vertical"): + self.side = "top" + self.dim = "height" + self.dir = "y" + self.cursor = "sb_v_double_arrow" + else: + raise ValueError, "Separator: orient should be h or v" + self.winfo_dim = "winfo_" + self.dim + self.master = master = Frame(master) + master.pack(expand=1, fill="both") + self.f1 = Frame(master) + self.f1.pack(expand=1, fill="both", side=self.side) + self.div = Frame(master, cursor=self.cursor) + self.div[self.dim] = self.thickness + self.div.pack(fill="both", side=self.side) + self.f2 = Frame(master) + self.f2.pack(expand=1, fill="both", side=self.side) + self.div.bind("<ButtonPress-1>", self.divider_press) + if bg: + ##self.f1["bg"] = bg + ##self.f2["bg"] = bg + self.div["bg"] = bg + + def parts(self): + return self.f1, self.f2 + + def divider_press(self, event): + self.press_event = event + self.f1.pack_propagate(0) + self.f2.pack_propagate(0) + for f in self.f1, self.f2: + for dim in "width", "height": + f[dim] = getattr(f, "winfo_"+dim)() + self.div.bind("<Motion>", self.div_motion) + self.div.bind("<ButtonRelease-1>", self.div_release) + self.div.grab_set() + + def div_motion(self, event): + delta = getattr(event, self.dir) - getattr(self.press_event, self.dir) + if delta: + dim1 = getattr(self.f1, self.winfo_dim)() + dim2 = getattr(self.f2, self.winfo_dim)() + delta = max(delta, self.min-dim1) + delta = min(delta, dim2-self.min) + dim1 = dim1 + delta + dim2 = dim2 - delta + self.f1[self.dim] = dim1 + self.f2[self.dim] = dim2 + + def div_release(self, event): + self.div_motion(event) + self.div.unbind("<Motion>") + self.div.grab_release() + +class VSeparator(Separator): + + def __init__(self, master, min=10, thickness=5, bg=None): + Separator.__init__(self, master, "v", min, thickness, bg) + +class HSeparator(Separator): + + def __init__(self, master, min=10, thickness=5, bg=None): + Separator.__init__(self, master, "h", min, thickness, bg) + +def main(): + root = Tk() + tlist = [] + outer = HSeparator(root, bg="red") + for part in outer.parts(): + inner = VSeparator(part, bg="blue") + for f in inner.parts(): + t = Text(f, width=40, height=10, borderwidth=0) + t.pack(fill="both", expand=1) + tlist.append(t) + tlist[0].insert("1.0", "Make your own Mondrian!") + tlist[1].insert("1.0", "Move the colored dividers...") + root.mainloop() + +if __name__ == '__main__': + main() diff --git a/Tools/idle/config-mac.txt b/Tools/idle/config-mac.txt new file mode 100644 index 0000000..ee36e13 --- /dev/null +++ b/Tools/idle/config-mac.txt @@ -0,0 +1,3 @@ +[EditorWindow] +font-name= monaco +font-size= 9 diff --git a/Tools/idle/config-unix.txt b/Tools/idle/config-unix.txt new file mode 100644 index 0000000..782965f --- /dev/null +++ b/Tools/idle/config-unix.txt @@ -0,0 +1,4 @@ +[EditorWindow] +font-name= courier +font-size= 10 +print-command=lpr %s diff --git a/Tools/idle/config-win.txt b/Tools/idle/config-win.txt new file mode 100644 index 0000000..aeb6ab9 --- /dev/null +++ b/Tools/idle/config-win.txt @@ -0,0 +1,4 @@ +[EditorWindow] +font-name: courier new +font-size: 10 +print-command=start /min notepad /p %s diff --git a/Tools/idle/config.txt b/Tools/idle/config.txt new file mode 100644 index 0000000..6f98a3e --- /dev/null +++ b/Tools/idle/config.txt @@ -0,0 +1,64 @@ +# IDLE reads several config files to determine user preferences. This +# file is the default config file. When IDLE starts, it will look in +# the following four files in order: +# config.txt the default config file +# config-[win/unix/mac].txt the generic platform config file +# config-[sys.platform].txt the specific platform config file +# ~/.idle the user config file +# XXX what about Windows? +# +# The last definition of each option is used. For example, you can +# override the default window size (80x24) by defining width and +# height options in the EditorWindow section of your ~/.idle file +# +# IDLE extensions can be enabled and disabled by adding them to one of +# the config files. To enable an extension, create a section with the +# same name as the extension, e.g. the [ParenMatch] section below. To +# disable an extension, either remove the section or add the 'enable' +# option with the value 0. + +[EditorWindow] +width= 80 +height= 24 +# fonts defined in config-[win/unix].txt + +[Colors] +normal-foreground= black +normal-background= white +# These color types are not explicitly defined= sync, todo, stdin +keyword-foreground= #ff7700 +comment-foreground= #dd0000 +string-foreground= #00aa00 +definition-foreground= #0000ff +hilite-foreground= #000068 +hilite-background= #006868 +break-foreground= #ff7777 +hit-foreground= #ffffff +hit-background= #000000 +stdout-foreground= blue +stderr-foreground= red +console-foreground= #770000 +error-background= #ff7777 +cursor-background= black + +[SearchBinding] + +[AutoIndent] + +[AutoExpand] + +[FormatParagraph] + +[ZoomHeight] + +[ScriptBinding] + +[CallTips] + +[ParenMatch] +enable= 0 +style= expression +flash-delay= 500 +bell= 1 +hilite-foreground= black +hilite-background= #43cd80 diff --git a/Tools/idle/eventparse.py b/Tools/idle/eventparse.py new file mode 100644 index 0000000..f253b2a --- /dev/null +++ b/Tools/idle/eventparse.py @@ -0,0 +1,89 @@ +#! /usr/bin/env python + +"""Parse event definitions out of comments in source files.""" + +import sys +import glob +import fileinput +import pprint + +def main(): + hits = [] + sublist = [] + args = sys.argv[1:] + if not args: + args = filter(lambda s: 'A' <= s[0] <= 'Z', glob.glob("*.py")) + if not args: + print "No arguments, no [A-Z]*.py files." + return 1 + for line in fileinput.input(args): + if line[:2] == '#$': + if not sublist: + sublist.append('file %s' % fileinput.filename()) + sublist.append('line %d' % fileinput.lineno()) + sublist.append(line[2:-1].strip()) + else: + if sublist: + hits.append(sublist) + sublist = [] + if sublist: + hits.append(sublist) + sublist = [] + dd = {} + for sublist in hits: + d = {} + for line in sublist: + words = line.split(None, 1) + if len(words) != 2: + continue + tag = words[0] + l = d.get(tag, []) + l.append(words[1]) + d[tag] = l + if d.has_key('event'): + keys = d['event'] + if len(keys) != 1: + print "Multiple event keys in", d + print 'File "%s", line %d' % (d['file'], d['line']) + key = keys[0] + if dd.has_key(key): + print "Duplicate event in", d + print 'File "%s", line %d' % (d['file'], d['line']) + return + dd[key] = d + else: + print "No event key in", d + print 'File "%s", line %d' % (d['file'], d['line']) + winevents = getevents(dd, "win") + unixevents = getevents(dd, "unix") + save = sys.stdout + f = open("keydefs.py", "w") + try: + sys.stdout = f + print "windows_keydefs = \\" + pprint.pprint(winevents) + print + print "unix_keydefs = \\" + pprint.pprint(unixevents) + finally: + sys.stdout = save + f.close() + +def getevents(dd, key): + res = {} + events = dd.keys() + events.sort() + for e in events: + d = dd[e] + if d.has_key(key) or d.has_key("all"): + list = [] + for x in d.get(key, []) + d.get("all", []): + list.append(x) + if key == "unix" and x[:5] == "<Alt-": + x = "<Meta-" + x[5:] + list.append(x) + res[e] = list + return res + +if __name__ == '__main__': + sys.exit(main()) diff --git a/Tools/idle/keydefs.py b/Tools/idle/keydefs.py new file mode 100644 index 0000000..9761258 --- /dev/null +++ b/Tools/idle/keydefs.py @@ -0,0 +1,57 @@ +windows_keydefs = \ +{'<<Copy>>': ['<Control-c>', '<Control-C>'], + '<<Cut>>': ['<Control-x>', '<Control-X>'], + '<<Paste>>': ['<Control-v>', '<Control-V>'], + '<<beginning-of-line>>': ['<Control-a>', '<Home>'], + '<<center-insert>>': ['<Control-l>'], + '<<close-all-windows>>': ['<Control-q>'], + '<<close-window>>': ['<Alt-F4>'], + '<<dump-undo-state>>': ['<Control-backslash>'], + '<<end-of-file>>': ['<Control-d>'], + '<<help>>': ['<F1>'], + '<<history-next>>': ['<Alt-n>'], + '<<history-previous>>': ['<Alt-p>'], + '<<interrupt-execution>>': ['<Control-c>'], + '<<open-class-browser>>': ['<Alt-c>'], + '<<open-module>>': ['<Alt-m>'], + '<<open-new-window>>': ['<Control-n>'], + '<<open-window-from-file>>': ['<Control-o>'], + '<<plain-newline-and-indent>>': ['<Control-j>'], + '<<print-window>>': ['<Control-p>'], + '<<redo>>': ['<Control-y>'], + '<<remove-selection>>': ['<Escape>'], + '<<save-copy-of-window-as-file>>': ['<Alt-Shift-s>'], + '<<save-window-as-file>>': ['<Alt-s>'], + '<<save-window>>': ['<Control-s>'], + '<<select-all>>': ['<Control-a>'], + '<<toggle-auto-coloring>>': ['<Control-slash>'], + '<<undo>>': ['<Control-z>']} + +unix_keydefs = \ +{'<<Copy>>': ['<Alt-w>', '<Meta-w>'], + '<<Cut>>': ['<Control-w>'], + '<<Paste>>': ['<Control-y>'], + '<<beginning-of-line>>': ['<Control-a>', '<Home>'], + '<<center-insert>>': ['<Control-l>'], + '<<close-all-windows>>': ['<Control-x><Control-c>'], + '<<close-window>>': ['<Control-x><Control-0>', '<Control-x><Key-0>'], + '<<do-nothing>>': ['<Control-x>'], + '<<dump-undo-state>>': ['<Control-backslash>'], + '<<end-of-file>>': ['<Control-d>'], + '<<help>>': ['<F1>'], + '<<history-next>>': ['<Alt-n>', '<Meta-n>'], + '<<history-previous>>': ['<Alt-p>', '<Meta-p>'], + '<<interrupt-execution>>': ['<Control-c>'], + '<<open-class-browser>>': ['<Control-x><Control-b>'], + '<<open-module>>': ['<Control-x><Control-m>'], + '<<open-new-window>>': ['<Control-x><Control-n>'], + '<<open-window-from-file>>': ['<Control-x><Control-f>'], + '<<plain-newline-and-indent>>': ['<Control-j>'], + '<<print-window>>': ['<Control-x><Control-p>'], + '<<redo>>': ['<Alt-z>', '<Meta-z>'], + '<<save-copy-of-window-as-file>>': ['<Control-x><w>'], + '<<save-window-as-file>>': ['<Control-x><Control-w>'], + '<<save-window>>': ['<Control-x><Control-s>'], + '<<select-all>>': ['<Alt-a>', '<Meta-a>'], + '<<toggle-auto-coloring>>': ['<Control-slash>'], + '<<undo>>': ['<Control-z>']} |