summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Tools/idle/AutoIndent.py551
-rw-r--r--Tools/idle/FrameViewer.py38
-rw-r--r--Tools/idle/IdleConf.py113
-rw-r--r--Tools/idle/MultiScrolledLists.py137
-rw-r--r--Tools/idle/OldStackViewer.py275
-rw-r--r--Tools/idle/RemoteInterp.py341
-rw-r--r--Tools/idle/SearchBinding.py97
-rw-r--r--Tools/idle/Separator.py92
-rw-r--r--Tools/idle/config-mac.txt3
-rw-r--r--Tools/idle/config-unix.txt4
-rw-r--r--Tools/idle/config-win.txt4
-rw-r--r--Tools/idle/config.txt64
-rw-r--r--Tools/idle/eventparse.py89
-rw-r--r--Tools/idle/keydefs.py57
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>']}