Get rid of old IDLE. Lib/idlelib rules!

This commit is contained in:
Guido van Rossum 2003-06-14 00:08:37 +00:00
parent 91012fe9b5
commit ecb544e95b
71 changed files with 0 additions and 10925 deletions

View File

@ -24,8 +24,6 @@ i18n Tools for internationalization. pygettext.py
and msgfmt.py generates a binary message catalog
from a catalog in text format.
idle A Tkinter-based Python IDE.
modulator Interactively generate boiler plate for an extension
module. Works easiest if you have Tk.

View File

@ -1,91 +0,0 @@
import string
import re
###$ event <<expand-word>>
###$ win <Alt-slash>
###$ unix <Alt-slash>
class AutoExpand:
keydefs = {
'<<expand-word>>': ['<Alt-slash>'],
}
unix_keydefs = {
'<<expand-word>>': ['<Meta-slash>', '<Alt-slash>'],
}
menudefs = [
('edit', [
('E_xpand word', '<<expand-word>>'),
]),
]
wordchars = string.ascii_letters + string.digits + "_"
def __init__(self, editwin):
self.text = editwin.text
self.state = None
def expand_word_event(self, event):
curinsert = self.text.index("insert")
curline = self.text.get("insert linestart", "insert lineend")
if not self.state:
words = self.getwords()
index = 0
else:
words, index, insert, line = self.state
if insert != curinsert or line != curline:
words = self.getwords()
index = 0
if not words:
self.text.bell()
return "break"
word = self.getprevword()
self.text.delete("insert - %d chars" % len(word), "insert")
newword = words[index]
index = (index + 1) % len(words)
if index == 0:
self.text.bell() # Warn we cycled around
self.text.insert("insert", newword)
curinsert = self.text.index("insert")
curline = self.text.get("insert linestart", "insert lineend")
self.state = words, index, curinsert, curline
return "break"
def getwords(self):
word = self.getprevword()
if not word:
return []
before = self.text.get("1.0", "insert wordstart")
wbefore = re.findall(r"\b" + word + r"\w+\b", before)
del before
after = self.text.get("insert wordend", "end")
wafter = re.findall(r"\b" + word + r"\w+\b", after)
del after
if not wbefore and not wafter:
return []
words = []
dict = {}
# search backwards through words before
wbefore.reverse()
for w in wbefore:
if dict.get(w):
continue
words.append(w)
dict[w] = w
# search onwards through words after
for w in wafter:
if dict.get(w):
continue
words.append(w)
dict[w] = w
words.append(word)
return words
def getprevword(self):
line = self.text.get("insert linestart", "insert")
i = len(line)
while i > 0 and line[i-1] in self.wordchars:
i = i-1
return line[i:]

View File

@ -1,551 +0,0 @@
#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

View File

@ -1,58 +0,0 @@
# This file defines the menu contents and key bindings. Note that
# there is additional configuration information in the EditorWindow
# class (and subclasses): the menus are created there based on the
# menu_specs (class) variable, and menus not created are silently
# skipped by the code here. This makes it possible to define the
# Debug menu here, which is only present in the PythonShell window.
import sys
from keydefs import *
menudefs = [
# underscore prefixes character to underscore
('file', [
('_New window', '<<open-new-window>>'),
('_Open...', '<<open-window-from-file>>'),
('Open _module...', '<<open-module>>'),
('Class _browser', '<<open-class-browser>>'),
('_Path browser', '<<open-path-browser>>'),
('Python shell', '<<open-python-shell>>'),
None,
('_Save', '<<save-window>>'),
('Save _As...', '<<save-window-as-file>>'),
('Save Co_py As...', '<<save-copy-of-window-as-file>>'),
None,
('_Print window', '<<print-window>>'),
None,
('_Close', '<<close-window>>'),
('E_xit', '<<close-all-windows>>'),
]),
('edit', [
('_Undo', '<<undo>>'),
('_Redo', '<<redo>>'),
None,
('Cu_t', '<<Cut>>'),
('_Copy', '<<Copy>>'),
('_Paste', '<<Paste>>'),
('Select _All', '<<select-all>>'),
]),
('debug', [
('_Go to file/line', '<<goto-file-line>>'),
('_Stack viewer', '<<open-stack-viewer>>'),
('!_Debugger', '<<toggle-debugger>>'),
('!_Auto-open stack viewer', '<<toggle-jit-stack-viewer>>' ),
]),
('help', [
('_Help...', '<<help>>'),
('Python _Documentation...', '<<python-docs>>'),
None,
('_About IDLE...', '<<about-idle>>'),
]),
]
if sys.platform == 'win32':
default_keydefs = windows_keydefs
else:
default_keydefs = unix_keydefs
del sys

View File

@ -1,76 +0,0 @@
# A CallTip window class for Tkinter/IDLE.
# After ToolTip.py, which uses ideas gleaned from PySol
# Used by the CallTips IDLE extension.
from Tkinter import *
class CallTip:
def __init__(self, widget):
self.widget = widget
self.tipwindow = None
self.id = None
self.x = self.y = 0
def showtip(self, text):
# SF bug 546078: IDLE calltips cause application error.
# There were crashes on various Windows flavors, and even a
# crashing X server on Linux, with very long calltips.
if len(text) >= 79:
text = text[:75] + ' ...'
self.text = text
if self.tipwindow or not self.text:
return
self.widget.see("insert")
x, y, cx, cy = self.widget.bbox("insert")
x = x + self.widget.winfo_rootx() + 2
y = y + cy + self.widget.winfo_rooty()
self.tipwindow = tw = Toplevel(self.widget)
tw.wm_overrideredirect(1)
tw.wm_geometry("+%d+%d" % (x, y))
label = Label(tw, text=self.text, justify=LEFT,
background="#ffffe0", relief=SOLID, borderwidth=1,
font = self.widget['font'])
label.pack()
def hidetip(self):
tw = self.tipwindow
self.tipwindow = None
if tw:
tw.destroy()
###############################
#
# Test Code
#
class container: # Conceptually an editor_window
def __init__(self):
root = Tk()
text = self.text = Text(root)
text.pack(side=LEFT, fill=BOTH, expand=1)
text.insert("insert", "string.split")
root.update()
self.calltip = CallTip(text)
text.event_add("<<calltip-show>>", "(")
text.event_add("<<calltip-hide>>", ")")
text.bind("<<calltip-show>>", self.calltip_show)
text.bind("<<calltip-hide>>", self.calltip_hide)
text.focus_set()
# root.mainloop() # not in idle
def calltip_show(self, event):
self.calltip.showtip("Hello world")
def calltip_hide(self, event):
self.calltip.hidetip()
def main():
# Test code
c=container()
if __name__=='__main__':
main()

View File

@ -1,197 +0,0 @@
# CallTips.py - An IDLE extension that provides "Call Tips" - ie, a floating window that
# displays parameter information as you open parens.
import string
import types
class CallTips:
menudefs = [
]
keydefs = {
'<<paren-open>>': ['<Key-parenleft>'],
'<<paren-close>>': ['<Key-parenright>'],
'<<check-calltip-cancel>>': ['<KeyRelease>'],
'<<calltip-cancel>>': ['<ButtonPress>', '<Key-Escape>'],
}
windows_keydefs = {
}
unix_keydefs = {
}
def __init__(self, editwin):
self.editwin = editwin
self.text = editwin.text
self.calltip = None
if hasattr(self.text, "make_calltip_window"):
self._make_calltip_window = self.text.make_calltip_window
else:
self._make_calltip_window = self._make_tk_calltip_window
def close(self):
self._make_calltip_window = None
# Makes a Tk based calltip window. Used by IDLE, but not Pythonwin.
# See __init__ above for how this is used.
def _make_tk_calltip_window(self):
import CallTipWindow
return CallTipWindow.CallTip(self.text)
def _remove_calltip_window(self):
if self.calltip:
self.calltip.hidetip()
self.calltip = None
def paren_open_event(self, event):
self._remove_calltip_window()
arg_text = get_arg_text(self.get_object_at_cursor())
if arg_text:
self.calltip_start = self.text.index("insert")
self.calltip = self._make_calltip_window()
self.calltip.showtip(arg_text)
return "" #so the event is handled normally.
def paren_close_event(self, event):
# Now just hides, but later we should check if other
# paren'd expressions remain open.
self._remove_calltip_window()
return "" #so the event is handled normally.
def check_calltip_cancel_event(self, event):
if self.calltip:
# If we have moved before the start of the calltip,
# or off the calltip line, then cancel the tip.
# (Later need to be smarter about multi-line, etc)
if self.text.compare("insert", "<=", self.calltip_start) or \
self.text.compare("insert", ">", self.calltip_start + " lineend"):
self._remove_calltip_window()
return "" #so the event is handled normally.
def calltip_cancel_event(self, event):
self._remove_calltip_window()
return "" #so the event is handled normally.
def get_object_at_cursor(self,
wordchars="._" + string.ascii_letters + string.digits):
# Usage of ascii_letters is necessary to avoid UnicodeErrors
# if chars contains non-ASCII.
# XXX - This needs to be moved to a better place
# so the "." attribute lookup code can also use it.
text = self.text
chars = text.get("insert linestart", "insert")
i = len(chars)
while i and chars[i-1] in wordchars:
i = i-1
word = chars[i:]
if word:
# How is this for a hack!
import sys, __main__
namespace = sys.modules.copy()
namespace.update(__main__.__dict__)
try:
return eval(word, namespace)
except:
pass
return None # Can't find an object.
def _find_constructor(class_ob):
# Given a class object, return a function object used for the
# constructor (ie, __init__() ) or None if we can't find one.
try:
return class_ob.__init__.im_func
except AttributeError:
for base in class_ob.__bases__:
rc = _find_constructor(base)
if rc is not None: return rc
return None
def get_arg_text(ob):
# Get a string describing the arguments for the given object.
argText = ""
if ob is not None:
argOffset = 0
if type(ob)==types.ClassType:
# Look for the highest __init__ in the class chain.
fob = _find_constructor(ob)
if fob is None:
fob = lambda: None
else:
argOffset = 1
elif type(ob)==types.MethodType:
# bit of a hack for methods - turn it into a function
# but we drop the "self" param.
fob = ob.im_func
argOffset = 1
else:
fob = ob
# Try and build one for Python defined functions
if type(fob) in [types.FunctionType, types.LambdaType]:
try:
realArgs = fob.func_code.co_varnames[argOffset:fob.func_code.co_argcount]
defaults = fob.func_defaults or []
defaults = list(map(lambda name: "=%s" % name, defaults))
defaults = [""] * (len(realArgs)-len(defaults)) + defaults
items = map(lambda arg, dflt: arg+dflt, realArgs, defaults)
if fob.func_code.co_flags & 0x4:
items.append("...")
if fob.func_code.co_flags & 0x8:
items.append("***")
argText = ", ".join(items)
argText = "(%s)" % argText
except:
pass
# See if we can use the docstring
doc = getattr(ob, "__doc__", "")
if doc:
while doc[:1] in " \t\n":
doc = doc[1:]
pos = doc.find("\n")
if pos < 0 or pos > 70:
pos = 70
if argText:
argText += "\n"
argText += doc[:pos]
return argText
#################################################
#
# Test code
#
if __name__=='__main__':
def t1(): "()"
def t2(a, b=None): "(a, b=None)"
def t3(a, *args): "(a, ...)"
def t4(*args): "(...)"
def t5(a, *args): "(a, ...)"
def t6(a, b=None, *args, **kw): "(a, b=None, ..., ***)"
class TC:
"(a=None, ...)"
def __init__(self, a=None, *b): "(a=None, ...)"
def t1(self): "()"
def t2(self, a, b=None): "(a, b=None)"
def t3(self, a, *args): "(a, ...)"
def t4(self, *args): "(...)"
def t5(self, a, *args): "(a, ...)"
def t6(self, a, b=None, *args, **kw): "(a, b=None, ..., ***)"
def test( tests ):
failed=[]
for t in tests:
expected = t.__doc__ + "\n" + t.__doc__
if get_arg_text(t) != expected:
failed.append(t)
print "%s - expected %s, but got %s" % (t, `expected`, `get_arg_text(t)`)
print "%d of %d tests failed" % (len(failed), len(tests))
tc = TC()
tests = t1, t2, t3, t4, t5, t6, \
TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6
test(tests)

File diff suppressed because it is too large Load Diff

View File

@ -1,217 +0,0 @@
"""Class browser.
XXX TO DO:
- reparse when source changed (maybe just a button would be OK?)
(or recheck on window popup)
- add popup menu with more options (e.g. doc strings, base classes, imports)
- show function argument list? (have to do pattern matching on source)
- should the classes and methods lists also be in the module's menu bar?
- add base classes to class browser tree
"""
import os
import sys
import pyclbr
import PyShell
from WindowList import ListedToplevel
from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
class ClassBrowser:
def __init__(self, flist, name, path):
# XXX This API should change, if the file doesn't end in ".py"
# XXX the code here is bogus!
self.name = name
self.file = os.path.join(path[0], self.name + ".py")
self.init(flist)
def close(self, event=None):
self.top.destroy()
self.node.destroy()
def init(self, flist):
self.flist = flist
# reset pyclbr
pyclbr._modules.clear()
# create top
self.top = top = ListedToplevel(flist.root)
top.protocol("WM_DELETE_WINDOW", self.close)
top.bind("<Escape>", self.close)
self.settitle()
top.focus_set()
# create scrolled canvas
sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1)
sc.frame.pack(expand=1, fill="both")
item = self.rootnode()
self.node = node = TreeNode(sc.canvas, None, item)
node.update()
node.expand()
def settitle(self):
self.top.wm_title("Class Browser - " + self.name)
self.top.wm_iconname("Class Browser")
def rootnode(self):
return ModuleBrowserTreeItem(self.file)
class ModuleBrowserTreeItem(TreeItem):
def __init__(self, file):
self.file = file
def GetText(self):
return os.path.basename(self.file)
def GetIconName(self):
return "python"
def GetSubList(self):
sublist = []
for name in self.listclasses():
item = ClassBrowserTreeItem(name, self.classes, self.file)
sublist.append(item)
return sublist
def OnDoubleClick(self):
if os.path.normcase(self.file[-3:]) != ".py":
return
if not os.path.exists(self.file):
return
PyShell.flist.open(self.file)
def IsExpandable(self):
return os.path.normcase(self.file[-3:]) == ".py"
def listclasses(self):
dir, file = os.path.split(self.file)
name, ext = os.path.splitext(file)
if os.path.normcase(ext) != ".py":
return []
try:
dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
except ImportError, msg:
return []
items = []
self.classes = {}
for key, cl in dict.items():
if cl.module == name:
s = key
if hasattr(cl, "super") and cl.super:
supers = []
for sup in cl.super:
if type(sup) is type(''):
sname = sup
else:
sname = sup.name
if sup.module != cl.module:
sname = "%s.%s" % (sup.module, sname)
supers.append(sname)
s = s + "(%s)" % ", ".join(supers)
items.append((cl.lineno, s))
self.classes[s] = cl
items.sort()
list = []
for item, s in items:
list.append(s)
return list
class ClassBrowserTreeItem(TreeItem):
def __init__(self, name, classes, file):
self.name = name
self.classes = classes
self.file = file
try:
self.cl = self.classes[self.name]
except (IndexError, KeyError):
self.cl = None
self.isfunction = isinstance(self.cl, pyclbr.Function)
def GetText(self):
if self.isfunction:
return "def " + self.name + "(...)"
else:
return "class " + self.name
def GetIconName(self):
if self.isfunction:
return "python"
else:
return "folder"
def IsExpandable(self):
try:
return bool(self.cl.methods)
except AttributeError:
return False
def GetSubList(self):
if not self.cl:
return []
sublist = []
for name in self.listmethods():
item = MethodBrowserTreeItem(name, self.cl, self.file)
sublist.append(item)
return sublist
def OnDoubleClick(self):
if not os.path.exists(self.file):
return
edit = PyShell.flist.open(self.file)
if hasattr(self.cl, 'lineno'):
lineno = self.cl.lineno
edit.gotoline(lineno)
def listmethods(self):
if not self.cl:
return []
items = []
for name, lineno in self.cl.methods.items():
items.append((lineno, name))
items.sort()
list = []
for item, name in items:
list.append(name)
return list
class MethodBrowserTreeItem(TreeItem):
def __init__(self, name, cl, file):
self.name = name
self.cl = cl
self.file = file
def GetText(self):
return "def " + self.name + "(...)"
def GetIconName(self):
return "python" # XXX
def IsExpandable(self):
return 0
def OnDoubleClick(self):
if not os.path.exists(self.file):
return
edit = PyShell.flist.open(self.file)
edit.gotoline(self.cl.methods[self.name])
def main():
try:
file = __file__
except NameError:
file = sys.argv[0]
if sys.argv[1:]:
file = sys.argv[1]
else:
file = sys.argv[0]
dir, file = os.path.split(file)
name = os.path.splitext(file)[0]
ClassBrowser(PyShell.flist, name, [dir])
if sys.stdin is sys.__stdin__:
mainloop()
if __name__ == "__main__":
main()

View File

@ -1,246 +0,0 @@
import time
import re
import keyword
from Tkinter import *
from Delegator import Delegator
from IdleConf import idleconf
#$ event <<toggle-auto-coloring>>
#$ win <Control-slash>
#$ unix <Control-slash>
DEBUG = 0
def any(name, list):
return "(?P<%s>" % name + "|".join(list) + ")"
def make_pat():
kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b"
comment = any("COMMENT", [r"#[^\n]*"])
sqstring = r"(\b[rR])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
dqstring = r'(\b[rR])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
sq3string = r"(\b[rR])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
dq3string = r'(\b[rR])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
string = any("STRING", [sq3string, dq3string, sqstring, dqstring])
return kw + "|" + comment + "|" + string + "|" + any("SYNC", [r"\n"])
prog = re.compile(make_pat(), re.S)
idprog = re.compile(r"\s+(\w+)", re.S)
asprog = re.compile(r".*?\b(as)\b", re.S)
class ColorDelegator(Delegator):
def __init__(self):
Delegator.__init__(self)
self.prog = prog
self.idprog = idprog
self.asprog = asprog
def setdelegate(self, delegate):
if self.delegate is not None:
self.unbind("<<toggle-auto-coloring>>")
Delegator.setdelegate(self, delegate)
if delegate is not None:
self.config_colors()
self.bind("<<toggle-auto-coloring>>", self.toggle_colorize_event)
self.notify_range("1.0", "end")
def config_colors(self):
for tag, cnf in self.tagdefs.items():
if cnf:
apply(self.tag_configure, (tag,), cnf)
self.tag_raise('sel')
cconf = idleconf.getsection('Colors')
tagdefs = {
"COMMENT": cconf.getcolor("comment"),
"KEYWORD": cconf.getcolor("keyword"),
"STRING": cconf.getcolor("string"),
"DEFINITION": cconf.getcolor("definition"),
"SYNC": cconf.getcolor("sync"),
"TODO": cconf.getcolor("todo"),
"BREAK": cconf.getcolor("break"),
# The following is used by ReplaceDialog:
"hit": cconf.getcolor("hit"),
}
def insert(self, index, chars, tags=None):
index = self.index(index)
self.delegate.insert(index, chars, tags)
self.notify_range(index, index + "+%dc" % len(chars))
def delete(self, index1, index2=None):
index1 = self.index(index1)
self.delegate.delete(index1, index2)
self.notify_range(index1)
after_id = None
allow_colorizing = 1
colorizing = 0
def notify_range(self, index1, index2=None):
self.tag_add("TODO", index1, index2)
if self.after_id:
if DEBUG: print "colorizing already scheduled"
return
if self.colorizing:
self.stop_colorizing = 1
if DEBUG: print "stop colorizing"
if self.allow_colorizing:
if DEBUG: print "schedule colorizing"
self.after_id = self.after(1, self.recolorize)
close_when_done = None # Window to be closed when done colorizing
def close(self, close_when_done=None):
if self.after_id:
after_id = self.after_id
self.after_id = None
if DEBUG: print "cancel scheduled recolorizer"
self.after_cancel(after_id)
self.allow_colorizing = 0
self.stop_colorizing = 1
if close_when_done:
if not self.colorizing:
close_when_done.destroy()
else:
self.close_when_done = close_when_done
def toggle_colorize_event(self, event):
if self.after_id:
after_id = self.after_id
self.after_id = None
if DEBUG: print "cancel scheduled recolorizer"
self.after_cancel(after_id)
if self.allow_colorizing and self.colorizing:
if DEBUG: print "stop colorizing"
self.stop_colorizing = 1
self.allow_colorizing = not self.allow_colorizing
if self.allow_colorizing and not self.colorizing:
self.after_id = self.after(1, self.recolorize)
if DEBUG:
print "auto colorizing turned", self.allow_colorizing and "on" or "off"
return "break"
def recolorize(self):
self.after_id = None
if not self.delegate:
if DEBUG: print "no delegate"
return
if not self.allow_colorizing:
if DEBUG: print "auto colorizing is off"
return
if self.colorizing:
if DEBUG: print "already colorizing"
return
try:
self.stop_colorizing = 0
self.colorizing = 1
if DEBUG: print "colorizing..."
t0 = time.clock()
self.recolorize_main()
t1 = time.clock()
if DEBUG: print "%.3f seconds" % (t1-t0)
finally:
self.colorizing = 0
if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"):
if DEBUG: print "reschedule colorizing"
self.after_id = self.after(1, self.recolorize)
if self.close_when_done:
top = self.close_when_done
self.close_when_done = None
top.destroy()
def recolorize_main(self):
next = "1.0"
while 1:
item = self.tag_nextrange("TODO", next)
if not item:
break
head, tail = item
self.tag_remove("SYNC", head, tail)
item = self.tag_prevrange("SYNC", head)
if item:
head = item[1]
else:
head = "1.0"
chars = ""
next = head
lines_to_get = 1
ok = 0
while not ok:
mark = next
next = self.index(mark + "+%d lines linestart" %
lines_to_get)
lines_to_get = min(lines_to_get * 2, 100)
ok = "SYNC" in self.tag_names(next + "-1c")
line = self.get(mark, next)
##print head, "get", mark, next, "->", `line`
if not line:
return
for tag in self.tagdefs.keys():
self.tag_remove(tag, mark, next)
chars = chars + line
m = self.prog.search(chars)
while m:
for key, value in m.groupdict().items():
if value:
a, b = m.span(key)
self.tag_add(key,
head + "+%dc" % a,
head + "+%dc" % b)
if value in ("def", "class"):
m1 = self.idprog.match(chars, b)
if m1:
a, b = m1.span(1)
self.tag_add("DEFINITION",
head + "+%dc" % a,
head + "+%dc" % b)
elif value == "import":
# color all the "as" words on same line;
# cheap approximation to the truth
while 1:
m1 = self.asprog.match(chars, b)
if not m1:
break
a, b = m1.span(1)
self.tag_add("KEYWORD",
head + "+%dc" % a,
head + "+%dc" % b)
m = self.prog.search(chars, m.end())
if "SYNC" in self.tag_names(next + "-1c"):
head = next
chars = ""
else:
ok = 0
if not ok:
# We're in an inconsistent state, and the call to
# update may tell us to stop. It may also change
# the correct value for "next" (since this is a
# line.col string, not a true mark). So leave a
# crumb telling the next invocation to resume here
# in case update tells us to leave.
self.tag_add("TODO", next)
self.update()
if self.stop_colorizing:
if DEBUG: print "colorizing stopped"
return
def main():
from Percolator import Percolator
root = Tk()
root.wm_protocol("WM_DELETE_WINDOW", root.quit)
text = Text(background="white")
text.pack(expand=1, fill="both")
text.focus_set()
p = Percolator(text)
d = ColorDelegator()
p.insertfilter(d)
root.mainloop()
if __name__ == "__main__":
main()

View File

@ -1,308 +0,0 @@
import os
import bdb
from Tkinter import *
from WindowList import ListedToplevel
import StackViewer
class Debugger(bdb.Bdb):
interacting = 0
vstack = vsource = vlocals = vglobals = None
def __init__(self, pyshell):
bdb.Bdb.__init__(self)
self.pyshell = pyshell
self.make_gui()
def canonic(self, filename):
# Canonicalize filename -- called by Bdb
return os.path.normcase(os.path.abspath(filename))
def close(self, event=None):
if self.interacting:
self.top.bell()
return
if self.stackviewer:
self.stackviewer.close(); self.stackviewer = None
self.pyshell.close_debugger()
self.top.destroy()
def run(self, *args):
try:
self.interacting = 1
return apply(bdb.Bdb.run, (self,) + args)
finally:
self.interacting = 0
def user_line(self, frame):
self.interaction(frame)
def user_return(self, frame, rv):
# XXX show rv?
##self.interaction(frame)
pass
def user_exception(self, frame, info):
self.interaction(frame, info)
def make_gui(self):
pyshell = self.pyshell
self.flist = pyshell.flist
self.root = root = pyshell.root
self.top = top =ListedToplevel(root)
self.top.wm_title("Debug Control")
self.top.wm_iconname("Debug")
top.wm_protocol("WM_DELETE_WINDOW", self.close)
self.top.bind("<Escape>", self.close)
#
self.bframe = bframe = Frame(top)
self.bframe.pack(anchor="w")
self.buttons = bl = []
#
self.bcont = b = Button(bframe, text="Go", command=self.cont)
bl.append(b)
self.bstep = b = Button(bframe, text="Step", command=self.step)
bl.append(b)
self.bnext = b = Button(bframe, text="Over", command=self.next)
bl.append(b)
self.bret = b = Button(bframe, text="Out", command=self.ret)
bl.append(b)
self.bret = b = Button(bframe, text="Quit", command=self.quit)
bl.append(b)
#
for b in bl:
b.configure(state="disabled")
b.pack(side="left")
#
self.cframe = cframe = Frame(bframe)
self.cframe.pack(side="left")
#
if not self.vstack:
self.__class__.vstack = BooleanVar(top)
self.vstack.set(1)
self.bstack = Checkbutton(cframe,
text="Stack", command=self.show_stack, variable=self.vstack)
self.bstack.grid(row=0, column=0)
if not self.vsource:
self.__class__.vsource = BooleanVar(top)
##self.vsource.set(1)
self.bsource = Checkbutton(cframe,
text="Source", command=self.show_source, variable=self.vsource)
self.bsource.grid(row=0, column=1)
if not self.vlocals:
self.__class__.vlocals = BooleanVar(top)
self.vlocals.set(1)
self.blocals = Checkbutton(cframe,
text="Locals", command=self.show_locals, variable=self.vlocals)
self.blocals.grid(row=1, column=0)
if not self.vglobals:
self.__class__.vglobals = BooleanVar(top)
##self.vglobals.set(1)
self.bglobals = Checkbutton(cframe,
text="Globals", command=self.show_globals, variable=self.vglobals)
self.bglobals.grid(row=1, column=1)
#
self.status = Label(top, anchor="w")
self.status.pack(anchor="w")
self.error = Label(top, anchor="w")
self.error.pack(anchor="w", fill="x")
self.errorbg = self.error.cget("background")
#
self.fstack = Frame(top, height=1)
self.fstack.pack(expand=1, fill="both")
self.flocals = Frame(top)
self.flocals.pack(expand=1, fill="both")
self.fglobals = Frame(top, height=1)
self.fglobals.pack(expand=1, fill="both")
#
if self.vstack.get():
self.show_stack()
if self.vlocals.get():
self.show_locals()
if self.vglobals.get():
self.show_globals()
frame = None
def interaction(self, frame, info=None):
self.frame = frame
code = frame.f_code
file = code.co_filename
base = os.path.basename(file)
lineno = frame.f_lineno
#
message = "%s:%s" % (base, lineno)
if code.co_name != "?":
message = "%s: %s()" % (message, code.co_name)
self.status.configure(text=message)
#
if info:
type, value, tb = info
try:
m1 = type.__name__
except AttributeError:
m1 = "%s" % str(type)
if value is not None:
try:
m1 = "%s: %s" % (m1, str(value))
except:
pass
bg = "yellow"
else:
m1 = ""
tb = None
bg = self.errorbg
self.error.configure(text=m1, background=bg)
#
sv = self.stackviewer
if sv:
stack, i = self.get_stack(self.frame, tb)
sv.load_stack(stack, i)
#
self.show_variables(1)
#
if self.vsource.get():
self.sync_source_line()
#
for b in self.buttons:
b.configure(state="normal")
#
self.top.tkraise()
self.root.mainloop()
#
for b in self.buttons:
b.configure(state="disabled")
self.status.configure(text="")
self.error.configure(text="", background=self.errorbg)
self.frame = None
def sync_source_line(self):
frame = self.frame
if not frame:
return
code = frame.f_code
file = code.co_filename
lineno = frame.f_lineno
if file[:1] + file[-1:] != "<>" and os.path.exists(file):
edit = self.flist.open(file)
if edit:
edit.gotoline(lineno)
def cont(self):
self.set_continue()
self.root.quit()
def step(self):
self.set_step()
self.root.quit()
def next(self):
self.set_next(self.frame)
self.root.quit()
def ret(self):
self.set_return(self.frame)
self.root.quit()
def quit(self):
self.set_quit()
self.root.quit()
stackviewer = None
def show_stack(self):
if not self.stackviewer and self.vstack.get():
self.stackviewer = sv = StackViewer.StackViewer(
self.fstack, self.flist, self)
if self.frame:
stack, i = self.get_stack(self.frame, None)
sv.load_stack(stack, i)
else:
sv = self.stackviewer
if sv and not self.vstack.get():
self.stackviewer = None
sv.close()
self.fstack['height'] = 1
def show_source(self):
if self.vsource.get():
self.sync_source_line()
def show_frame(self, (frame, lineno)):
self.frame = frame
self.show_variables()
localsviewer = None
globalsviewer = None
def show_locals(self):
lv = self.localsviewer
if self.vlocals.get():
if not lv:
self.localsviewer = StackViewer.NamespaceViewer(
self.flocals, "Locals")
else:
if lv:
self.localsviewer = None
lv.close()
self.flocals['height'] = 1
self.show_variables()
def show_globals(self):
gv = self.globalsviewer
if self.vglobals.get():
if not gv:
self.globalsviewer = StackViewer.NamespaceViewer(
self.fglobals, "Globals")
else:
if gv:
self.globalsviewer = None
gv.close()
self.fglobals['height'] = 1
self.show_variables()
def show_variables(self, force=0):
lv = self.localsviewer
gv = self.globalsviewer
frame = self.frame
if not frame:
ldict = gdict = None
else:
ldict = frame.f_locals
gdict = frame.f_globals
if lv and gv and ldict is gdict:
ldict = None
if lv:
lv.load_dict(ldict, force)
if gv:
gv.load_dict(gdict, force)
def set_breakpoint_here(self, edit):
text = edit.text
filename = edit.io.filename
if not filename:
text.bell()
return
lineno = int(float(text.index("insert")))
msg = self.set_break(filename, lineno)
if msg:
text.bell()
return
text.tag_add("BREAK", "insert linestart", "insert lineend +1char")
# A literal copy of Bdb.set_break() without the print statement at the end
def set_break(self, filename, lineno, temporary=0, cond = None):
import linecache # Import as late as possible
filename = self.canonic(filename)
line = linecache.getline(filename, lineno)
if not line:
return 'That line does not exist!'
if not self.breaks.has_key(filename):
self.breaks[filename] = []
list = self.breaks[filename]
if not lineno in list:
list.append(lineno)
bp = bdb.Breakpoint(filename, lineno, temporary, cond)

View File

@ -1,33 +0,0 @@
class Delegator:
# The cache is only used to be able to change delegates!
def __init__(self, delegate=None):
self.delegate = delegate
self.__cache = {}
def __getattr__(self, name):
attr = getattr(self.delegate, name) # May raise AttributeError
setattr(self, name, attr)
self.__cache[name] = attr
return attr
def resetcache(self):
for key in self.__cache.keys():
try:
delattr(self, key)
except AttributeError:
pass
self.__cache.clear()
def cachereport(self):
keys = self.__cache.keys()
keys.sort()
print keys
def setdelegate(self, delegate):
self.resetcache()
self.delegate = delegate
def getdelegate(self):
return self.delegate

View File

@ -1,756 +0,0 @@
import sys
import os
import re
import imp
from Tkinter import *
import tkSimpleDialog
import tkMessageBox
import webbrowser
import idlever
import WindowList
from IdleConf import idleconf
# The default tab setting for a Text widget, in average-width characters.
TK_TABWIDTH_DEFAULT = 8
# File menu
#$ event <<open-module>>
#$ win <Alt-m>
#$ unix <Control-x><Control-m>
#$ event <<open-class-browser>>
#$ win <Alt-c>
#$ unix <Control-x><Control-b>
#$ event <<open-path-browser>>
#$ event <<close-window>>
#$ unix <Control-x><Control-0>
#$ unix <Control-x><Key-0>
#$ win <Alt-F4>
# Edit menu
#$ event <<Copy>>
#$ win <Control-c>
#$ unix <Alt-w>
#$ event <<Cut>>
#$ win <Control-x>
#$ unix <Control-w>
#$ event <<Paste>>
#$ win <Control-v>
#$ unix <Control-y>
#$ event <<select-all>>
#$ win <Alt-a>
#$ unix <Alt-a>
# Help menu
#$ event <<help>>
#$ win <F1>
#$ unix <F1>
#$ event <<about-idle>>
# Events without menu entries
#$ event <<remove-selection>>
#$ win <Escape>
#$ event <<center-insert>>
#$ win <Control-l>
#$ unix <Control-l>
#$ event <<do-nothing>>
#$ unix <Control-x>
about_title = "About IDLE"
about_text = """\
IDLE %s
An Integrated DeveLopment Environment for Python
by Guido van Rossum
""" % idlever.IDLE_VERSION
def _find_module(fullname, path=None):
"""Version of imp.find_module() that handles hierarchical module names"""
file = None
for tgt in fullname.split('.'):
if file is not None:
file.close() # close intermediate files
(file, filename, descr) = imp.find_module(tgt, path)
if descr[2] == imp.PY_SOURCE:
break # find but not load the source file
module = imp.load_module(tgt, file, filename, descr)
try:
path = module.__path__
except AttributeError:
raise ImportError, 'No source for module ' + module.__name__
return file, filename, descr
class EditorWindow:
from Percolator import Percolator
from ColorDelegator import ColorDelegator
from UndoDelegator import UndoDelegator
from IOBinding import IOBinding
import Bindings
from Tkinter import Toplevel
from MultiStatusBar import MultiStatusBar
about_title = about_title
about_text = about_text
vars = {}
runnable = False # Shell window cannot Import Module or Run Script
def __init__(self, flist=None, filename=None, key=None, root=None):
edconf = idleconf.getsection('EditorWindow')
coconf = idleconf.getsection('Colors')
self.flist = flist
root = root or flist.root
self.root = root
if flist:
self.vars = flist.vars
self.menubar = Menu(root)
self.top = top = self.Toplevel(root, menu=self.menubar)
self.vbar = vbar = Scrollbar(top, name='vbar')
self.text_frame = text_frame = Frame(top)
self.text = text = Text(text_frame, name='text', padx=5,
foreground=coconf.getdef('normal-foreground'),
background=coconf.getdef('normal-background'),
highlightcolor=coconf.getdef('hilite-foreground'),
highlightbackground=coconf.getdef('hilite-background'),
insertbackground=coconf.getdef('cursor-background'),
width=edconf.getint('width'),
height=edconf.getint('height'),
wrap="none")
self.createmenubar()
self.apply_bindings()
self.top.protocol("WM_DELETE_WINDOW", self.close)
self.top.bind("<<close-window>>", self.close_event)
text.bind("<<center-insert>>", self.center_insert_event)
text.bind("<<help>>", self.help_dialog)
text.bind("<<python-docs>>", self.python_docs)
text.bind("<<about-idle>>", self.about_dialog)
text.bind("<<open-module>>", self.open_module)
text.bind("<<do-nothing>>", lambda event: "break")
text.bind("<<select-all>>", self.select_all)
text.bind("<<remove-selection>>", self.remove_selection)
text.bind("<3>", self.right_menu_event)
if flist:
flist.inversedict[self] = key
if key:
flist.dict[key] = self
text.bind("<<open-new-window>>", self.flist.new_callback)
text.bind("<<close-all-windows>>", self.flist.close_all_callback)
text.bind("<<open-class-browser>>", self.open_class_browser)
text.bind("<<open-path-browser>>", self.open_path_browser)
vbar['command'] = text.yview
vbar.pack(side=RIGHT, fill=Y)
text['yscrollcommand'] = vbar.set
text['font'] = edconf.get('font-name'), edconf.get('font-size')
text_frame.pack(side=LEFT, fill=BOTH, expand=1)
text.pack(side=TOP, fill=BOTH, expand=1)
text.focus_set()
self.per = per = self.Percolator(text)
if self.ispythonsource(filename):
self.color = color = self.ColorDelegator(); per.insertfilter(color)
##print "Initial colorizer"
else:
##print "No initial colorizer"
self.color = None
self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
self.io = io = self.IOBinding(self)
text.undo_block_start = undo.undo_block_start
text.undo_block_stop = undo.undo_block_stop
undo.set_saved_change_hook(self.saved_change_hook)
io.set_filename_change_hook(self.filename_change_hook)
if filename:
if os.path.exists(filename):
io.loadfile(filename)
else:
io.set_filename(filename)
self.saved_change_hook()
self.load_extensions()
menu = self.menudict.get('windows')
if menu:
end = menu.index("end")
if end is None:
end = -1
if end >= 0:
menu.add_separator()
end = end + 1
self.wmenu_end = end
WindowList.register_callback(self.postwindowsmenu)
# Some abstractions so IDLE extensions are cross-IDE
self.askyesno = tkMessageBox.askyesno
self.askinteger = tkSimpleDialog.askinteger
self.showerror = tkMessageBox.showerror
if self.extensions.has_key('AutoIndent'):
self.extensions['AutoIndent'].set_indentation_params(
self.ispythonsource(filename))
self.set_status_bar()
def set_status_bar(self):
self.status_bar = self.MultiStatusBar(self.text_frame)
self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
self.status_bar.pack(side=BOTTOM, fill=X)
self.text.bind('<KeyRelease>', self.set_line_and_column)
self.text.bind('<ButtonRelease>', self.set_line_and_column)
self.text.after_idle(self.set_line_and_column)
def set_line_and_column(self, event=None):
line, column = self.text.index(INSERT).split('.')
self.status_bar.set_label('column', 'Col: %s' % column)
self.status_bar.set_label('line', 'Ln: %s' % line)
def wakeup(self):
if self.top.wm_state() == "iconic":
self.top.wm_deiconify()
else:
self.top.tkraise()
self.text.focus_set()
menu_specs = [
("file", "_File"),
("edit", "_Edit"),
("windows", "_Windows"),
("help", "_Help"),
]
def createmenubar(self):
mbar = self.menubar
self.menudict = menudict = {}
for name, label in self.menu_specs:
underline, label = prepstr(label)
menudict[name] = menu = Menu(mbar, name=name)
mbar.add_cascade(label=label, menu=menu, underline=underline)
self.fill_menus()
def postwindowsmenu(self):
# Only called when Windows menu exists
# XXX Actually, this Just-In-Time updating interferes badly
# XXX with the tear-off feature. It would be better to update
# XXX all Windows menus whenever the list of windows changes.
menu = self.menudict['windows']
end = menu.index("end")
if end is None:
end = -1
if end > self.wmenu_end:
menu.delete(self.wmenu_end+1, end)
WindowList.add_windows_to_menu(menu)
rmenu = None
def right_menu_event(self, event):
self.text.tag_remove("sel", "1.0", "end")
self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
if not self.rmenu:
self.make_rmenu()
rmenu = self.rmenu
self.event = event
iswin = sys.platform[:3] == 'win'
if iswin:
self.text.config(cursor="arrow")
rmenu.tk_popup(event.x_root, event.y_root)
if iswin:
self.text.config(cursor="ibeam")
rmenu_specs = [
# ("Label", "<<virtual-event>>"), ...
("Close", "<<close-window>>"), # Example
]
def make_rmenu(self):
rmenu = Menu(self.text, tearoff=0)
for label, eventname in self.rmenu_specs:
def command(text=self.text, eventname=eventname):
text.event_generate(eventname)
rmenu.add_command(label=label, command=command)
self.rmenu = rmenu
def about_dialog(self, event=None):
tkMessageBox.showinfo(self.about_title, self.about_text,
master=self.text)
helpfile = "help.txt"
def help_dialog(self, event=None):
try:
helpfile = os.path.join(os.path.dirname(__file__), self.helpfile)
except NameError:
helpfile = self.helpfile
if self.flist:
self.flist.open(helpfile)
else:
self.io.loadfile(helpfile)
help_url = "http://www.python.org/doc/current/"
if sys.platform[:3] == "win":
fn = os.path.dirname(__file__)
fn = os.path.join(fn, os.pardir, os.pardir, "pythlp.chm")
fn = os.path.normpath(fn)
if os.path.isfile(fn):
help_url = fn
else:
fn = os.path.dirname(__file__)
fn = os.path.join(fn, os.pardir, os.pardir, "Doc", "index.html")
fn = os.path.normpath(fn)
if os.path.isfile(fn):
help_url = fn
del fn
def python_docs(self, event=None):
os.startfile(self.help_url)
else:
def python_docs(self, event=None):
webbrowser.open(self.help_url)
def select_all(self, event=None):
self.text.tag_add("sel", "1.0", "end-1c")
self.text.mark_set("insert", "1.0")
self.text.see("insert")
return "break"
def remove_selection(self, event=None):
self.text.tag_remove("sel", "1.0", "end")
self.text.see("insert")
def open_module(self, event=None):
# XXX Shouldn't this be in IOBinding or in FileList?
try:
name = self.text.get("sel.first", "sel.last")
except TclError:
name = ""
else:
name = name.strip()
if not name:
name = tkSimpleDialog.askstring("Module",
"Enter the name of a Python module\n"
"to search on sys.path and open:",
parent=self.text)
if name:
name = name.strip()
if not name:
return
# XXX Ought to insert current file's directory in front of path
try:
(f, file, (suffix, mode, type)) = _find_module(name)
except (NameError, ImportError), msg:
tkMessageBox.showerror("Import error", str(msg), parent=self.text)
return
if type != imp.PY_SOURCE:
tkMessageBox.showerror("Unsupported type",
"%s is not a source module" % name, parent=self.text)
return
if f:
f.close()
if self.flist:
self.flist.open(file)
else:
self.io.loadfile(file)
def open_class_browser(self, event=None):
filename = self.io.filename
if not filename:
tkMessageBox.showerror(
"No filename",
"This buffer has no associated filename",
master=self.text)
self.text.focus_set()
return None
head, tail = os.path.split(filename)
base, ext = os.path.splitext(tail)
import ClassBrowser
ClassBrowser.ClassBrowser(self.flist, base, [head])
def open_path_browser(self, event=None):
import PathBrowser
PathBrowser.PathBrowser(self.flist)
def gotoline(self, lineno):
if lineno is not None and lineno > 0:
self.text.mark_set("insert", "%d.0" % lineno)
self.text.tag_remove("sel", "1.0", "end")
self.text.tag_add("sel", "insert", "insert +1l")
self.center()
def ispythonsource(self, filename):
if not filename:
return True
base, ext = os.path.splitext(os.path.basename(filename))
if os.path.normcase(ext) in (".py", ".pyw"):
return True
try:
f = open(filename)
line = f.readline()
f.close()
except IOError:
return False
return line.startswith('#!') and 'python' in line
def close_hook(self):
if self.flist:
self.flist.close_edit(self)
def set_close_hook(self, close_hook):
self.close_hook = close_hook
def filename_change_hook(self):
if self.flist:
self.flist.filename_changed_edit(self)
self.saved_change_hook()
if self.ispythonsource(self.io.filename):
self.addcolorizer()
else:
self.rmcolorizer()
def addcolorizer(self):
if self.color:
return
##print "Add colorizer"
self.per.removefilter(self.undo)
self.color = self.ColorDelegator()
self.per.insertfilter(self.color)
self.per.insertfilter(self.undo)
def rmcolorizer(self):
if not self.color:
return
##print "Remove colorizer"
self.per.removefilter(self.undo)
self.per.removefilter(self.color)
self.color = None
self.per.insertfilter(self.undo)
def saved_change_hook(self):
short = self.short_title()
long = self.long_title()
if short and long:
title = short + " - " + long
elif short:
title = short
elif long:
title = long
else:
title = "Untitled"
icon = short or long or title
if not self.get_saved():
title = "*%s*" % title
icon = "*%s" % icon
self.top.wm_title(title)
self.top.wm_iconname(icon)
def get_saved(self):
return self.undo.get_saved()
def set_saved(self, flag):
self.undo.set_saved(flag)
def reset_undo(self):
self.undo.reset_undo()
def short_title(self):
filename = self.io.filename
if filename:
filename = os.path.basename(filename)
return filename
def long_title(self):
return self.io.filename or ""
def center_insert_event(self, event):
self.center()
def center(self, mark="insert"):
text = self.text
top, bot = self.getwindowlines()
lineno = self.getlineno(mark)
height = bot - top
newtop = max(1, lineno - height//2)
text.yview(float(newtop))
def getwindowlines(self):
text = self.text
top = self.getlineno("@0,0")
bot = self.getlineno("@0,65535")
if top == bot and text.winfo_height() == 1:
# Geometry manager hasn't run yet
height = int(text['height'])
bot = top + height - 1
return top, bot
def getlineno(self, mark="insert"):
text = self.text
return int(float(text.index(mark)))
def close_event(self, event):
self.close()
def maybesave(self):
if self.io:
return self.io.maybesave()
def close(self):
self.top.wm_deiconify()
self.top.tkraise()
reply = self.maybesave()
if reply != "cancel":
self._close()
return reply
def _close(self):
WindowList.unregister_callback(self.postwindowsmenu)
if self.close_hook:
self.close_hook()
self.flist = None
colorizing = 0
self.unload_extensions()
self.io.close(); self.io = None
self.undo = None # XXX
if self.color:
colorizing = self.color.colorizing
doh = colorizing and self.top
self.color.close(doh) # Cancel colorization
self.text = None
self.vars = None
self.per.close(); self.per = None
if not colorizing:
self.top.destroy()
def load_extensions(self):
self.extensions = {}
self.load_standard_extensions()
def unload_extensions(self):
for ins in self.extensions.values():
if hasattr(ins, "close"):
ins.close()
self.extensions = {}
def load_standard_extensions(self):
for name in self.get_standard_extension_names():
try:
self.load_extension(name)
except:
print "Failed to load extension", `name`
import traceback
traceback.print_exc()
def get_standard_extension_names(self):
return idleconf.getextensions()
def load_extension(self, name):
mod = __import__(name, globals(), locals(), [])
cls = getattr(mod, name)
ins = cls(self)
self.extensions[name] = ins
kdnames = ["keydefs"]
if sys.platform == 'win32':
kdnames.append("windows_keydefs")
elif sys.platform == 'mac':
kdnames.append("mac_keydefs")
else:
kdnames.append("unix_keydefs")
keydefs = {}
for kdname in kdnames:
if hasattr(ins, kdname):
keydefs.update(getattr(ins, kdname))
if keydefs:
self.apply_bindings(keydefs)
for vevent in keydefs.keys():
methodname = vevent.replace("-", "_")
while methodname[:1] == '<':
methodname = methodname[1:]
while methodname[-1:] == '>':
methodname = methodname[:-1]
methodname = methodname + "_event"
if hasattr(ins, methodname):
self.text.bind(vevent, getattr(ins, methodname))
if hasattr(ins, "menudefs"):
self.fill_menus(ins.menudefs, keydefs)
return ins
def apply_bindings(self, keydefs=None):
if keydefs is None:
keydefs = self.Bindings.default_keydefs
text = self.text
text.keydefs = keydefs
for event, keylist in keydefs.items():
if keylist:
apply(text.event_add, (event,) + tuple(keylist))
def fill_menus(self, defs=None, keydefs=None):
# Fill the menus. Menus that are absent or None in
# self.menudict are ignored.
if defs is None:
defs = self.Bindings.menudefs
if keydefs is None:
keydefs = self.Bindings.default_keydefs
menudict = self.menudict
text = self.text
for mname, itemlist in defs:
menu = menudict.get(mname)
if not menu:
continue
for item in itemlist:
if not item:
menu.add_separator()
else:
label, event = item
checkbutton = (label[:1] == '!')
if checkbutton:
label = label[1:]
underline, label = prepstr(label)
accelerator = get_accelerator(keydefs, event)
def command(text=text, event=event):
text.event_generate(event)
if checkbutton:
var = self.getrawvar(event, BooleanVar)
menu.add_checkbutton(label=label, underline=underline,
command=command, accelerator=accelerator,
variable=var)
else:
menu.add_command(label=label, underline=underline,
command=command, accelerator=accelerator)
def getvar(self, name):
var = self.getrawvar(name)
if var:
return var.get()
def setvar(self, name, value, vartype=None):
var = self.getrawvar(name, vartype)
if var:
var.set(value)
def getrawvar(self, name, vartype=None):
var = self.vars.get(name)
if not var and vartype:
self.vars[name] = var = vartype(self.text)
return var
# Tk implementations of "virtual text methods" -- each platform
# reusing IDLE's support code needs to define these for its GUI's
# flavor of widget.
# Is character at text_index in a Python string? Return 0 for
# "guaranteed no", true for anything else. This info is expensive
# to compute ab initio, but is probably already known by the
# platform's colorizer.
def is_char_in_string(self, text_index):
if self.color:
# Return true iff colorizer hasn't (re)gotten this far
# yet, or the character is tagged as being in a string
return self.text.tag_prevrange("TODO", text_index) or \
"STRING" in self.text.tag_names(text_index)
else:
# The colorizer is missing: assume the worst
return 1
# If a selection is defined in the text widget, return (start,
# end) as Tkinter text indices, otherwise return (None, None)
def get_selection_indices(self):
try:
first = self.text.index("sel.first")
last = self.text.index("sel.last")
return first, last
except TclError:
return None, None
# Return the text widget's current view of what a tab stop means
# (equivalent width in spaces).
def get_tabwidth(self):
current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
return int(current)
# Set the text widget's current view of what a tab stop means.
def set_tabwidth(self, newtabwidth):
text = self.text
if self.get_tabwidth() != newtabwidth:
pixels = text.tk.call("font", "measure", text["font"],
"-displayof", text.master,
"n" * newtabwidth)
text.configure(tabs=pixels)
def prepstr(s):
# Helper to extract the underscore from a string, e.g.
# prepstr("Co_py") returns (2, "Copy").
i = s.find('_')
if i >= 0:
s = s[:i] + s[i+1:]
return i, s
keynames = {
'bracketleft': '[',
'bracketright': ']',
'slash': '/',
}
def get_accelerator(keydefs, event):
keylist = keydefs.get(event)
if not keylist:
return ""
s = keylist[0]
s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s)
s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
s = re.sub("Key-", "", s)
s = re.sub("Control-", "Ctrl-", s)
s = re.sub("-", "+", s)
s = re.sub("><", " ", s)
s = re.sub("<", "", s)
s = re.sub(">", "", s)
return s
def fixwordbreaks(root):
# Make sure that Tk's double-click and next/previous word
# operations use our definition of a word (i.e. an identifier)
tk = root.tk
tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
def test():
root = Tk()
fixwordbreaks(root)
root.withdraw()
if sys.argv[1:]:
filename = sys.argv[1]
else:
filename = None
edit = EditorWindow(root=root, filename=filename)
edit.set_close_hook(root.quit)
root.mainloop()
root.destroy()
if __name__ == '__main__':
test()

View File

@ -1,138 +0,0 @@
import os
from Tkinter import *
import tkMessageBox
import WindowList
#$ event <<open-new-window>>
#$ win <Control-n>
#$ unix <Control-x><Control-n>
# (This is labeled as 'Exit'in the File menu)
#$ event <<close-all-windows>>
#$ win <Control-q>
#$ unix <Control-x><Control-c>
class FileList:
from EditorWindow import EditorWindow
EditorWindow.Toplevel = WindowList.ListedToplevel # XXX Patch it!
def __init__(self, root):
self.root = root
self.dict = {}
self.inversedict = {}
self.vars = {} # For EditorWindow.getrawvar (shared Tcl variables)
def open(self, filename):
assert filename
filename = self.canonize(filename)
if os.path.isdir(filename):
tkMessageBox.showerror(
"Is A Directory",
"The path %s is a directory." % `filename`,
master=self.root)
return None
key = os.path.normcase(filename)
if self.dict.has_key(key):
edit = self.dict[key]
edit.wakeup()
return edit
if not os.path.exists(filename):
tkMessageBox.showinfo(
"New File",
"Opening non-existent file %s" % `filename`,
master=self.root)
return self.EditorWindow(self, filename, key)
def gotofileline(self, filename, lineno=None):
edit = self.open(filename)
if edit is not None and lineno is not None:
edit.gotoline(lineno)
def new(self):
return self.EditorWindow(self)
def new_callback(self, event):
self.new()
return "break"
def close_all_callback(self, event):
for edit in self.inversedict.keys():
reply = edit.close()
if reply == "cancel":
break
return "break"
def close_edit(self, edit):
try:
key = self.inversedict[edit]
except KeyError:
print "Don't know this EditorWindow object. (close)"
return
if key:
del self.dict[key]
del self.inversedict[edit]
if not self.inversedict:
self.root.quit()
def filename_changed_edit(self, edit):
edit.saved_change_hook()
try:
key = self.inversedict[edit]
except KeyError:
print "Don't know this EditorWindow object. (rename)"
return
filename = edit.io.filename
if not filename:
if key:
del self.dict[key]
self.inversedict[edit] = None
return
filename = self.canonize(filename)
newkey = os.path.normcase(filename)
if newkey == key:
return
if self.dict.has_key(newkey):
conflict = self.dict[newkey]
self.inversedict[conflict] = None
tkMessageBox.showerror(
"Name Conflict",
"You now have multiple edit windows open for %s" % `filename`,
master=self.root)
self.dict[newkey] = edit
self.inversedict[edit] = newkey
if key:
try:
del self.dict[key]
except KeyError:
pass
def canonize(self, filename):
if not os.path.isabs(filename):
try:
pwd = os.getcwd()
except os.error:
pass
else:
filename = os.path.join(pwd, filename)
return os.path.normpath(filename)
def _test():
from EditorWindow import fixwordbreaks
import sys
root = Tk()
fixwordbreaks(root)
root.withdraw()
flist = FileList(root)
if sys.argv[1:]:
for filename in sys.argv[1:]:
flist.open(filename)
else:
flist.new()
if flist.inversedict:
root.mainloop()
if __name__ == '__main__':
_test()

View File

@ -1,154 +0,0 @@
# Extension to format a paragraph
# Does basic, standard text formatting, and also understands Python
# comment blocks. Thus, for editing Python source code, this
# extension is really only suitable for reformatting these comment
# blocks or triple-quoted strings.
# Known problems with comment reformatting:
# * If there is a selection marked, and the first line of the
# selection is not complete, the block will probably not be detected
# as comments, and will have the normal "text formatting" rules
# applied.
# * If a comment block has leading whitespace that mixes tabs and
# spaces, they will not be considered part of the same block.
# * Fancy comments, like this bulleted list, arent handled :-)
import re
class FormatParagraph:
menudefs = [
('edit', [
('Format Paragraph', '<<format-paragraph>>'),
])
]
keydefs = {
'<<format-paragraph>>': ['<Alt-q>'],
}
unix_keydefs = {
'<<format-paragraph>>': ['<Meta-q>'],
}
def __init__(self, editwin):
self.editwin = editwin
def close(self):
self.editwin = None
def format_paragraph_event(self, event):
text = self.editwin.text
first, last = self.editwin.get_selection_indices()
if first and last:
data = text.get(first, last)
comment_header = ''
else:
first, last, comment_header, data = \
find_paragraph(text, text.index("insert"))
if comment_header:
# Reformat the comment lines - convert to text sans header.
lines = data.split("\n")
lines = map(lambda st, l=len(comment_header): st[l:], lines)
data = "\n".join(lines)
# Reformat to 70 chars or a 20 char width, whichever is greater.
format_width = max(70-len(comment_header), 20)
newdata = reformat_paragraph(data, format_width)
# re-split and re-insert the comment header.
newdata = newdata.split("\n")
# If the block ends in a \n, we dont want the comment
# prefix inserted after it. (Im not sure it makes sense to
# reformat a comment block that isnt made of complete
# lines, but whatever!) Can't think of a clean soltution,
# so we hack away
block_suffix = ""
if not newdata[-1]:
block_suffix = "\n"
newdata = newdata[:-1]
builder = lambda item, prefix=comment_header: prefix+item
newdata = '\n'.join(map(builder, newdata)) + block_suffix
else:
# Just a normal text format
newdata = reformat_paragraph(data)
text.tag_remove("sel", "1.0", "end")
if newdata != data:
text.mark_set("insert", first)
text.undo_block_start()
text.delete(first, last)
text.insert(first, newdata)
text.undo_block_stop()
else:
text.mark_set("insert", last)
text.see("insert")
def find_paragraph(text, mark):
lineno, col = map(int, mark.split("."))
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line):
lineno = lineno + 1
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
first_lineno = lineno
comment_header = get_comment_header(line)
comment_header_len = len(comment_header)
while get_comment_header(line)==comment_header and \
not is_all_white(line[comment_header_len:]):
lineno = lineno + 1
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
last = "%d.0" % lineno
# Search back to beginning of paragraph
lineno = first_lineno - 1
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
while lineno > 0 and \
get_comment_header(line)==comment_header and \
not is_all_white(line[comment_header_len:]):
lineno = lineno - 1
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
first = "%d.0" % (lineno+1)
return first, last, comment_header, text.get(first, last)
def reformat_paragraph(data, limit=70):
lines = data.split("\n")
i = 0
n = len(lines)
while i < n and is_all_white(lines[i]):
i = i+1
if i >= n:
return data
indent1 = get_indent(lines[i])
if i+1 < n and not is_all_white(lines[i+1]):
indent2 = get_indent(lines[i+1])
else:
indent2 = indent1
new = lines[:i]
partial = indent1
while i < n and not is_all_white(lines[i]):
# XXX Should take double space after period (etc.) into account
words = re.split("(\s+)", lines[i])
for j in range(0, len(words), 2):
word = words[j]
if not word:
continue # Can happen when line ends in whitespace
if len((partial + word).expandtabs()) > limit and \
partial != indent1:
new.append(partial.rstrip())
partial = indent2
partial = partial + word + " "
if j+1 < len(words) and words[j+1] != " ":
partial = partial + " "
i = i+1
new.append(partial.rstrip())
# XXX Should reformat remaining paragraphs as well
new.extend(lines[i:])
return "\n".join(new)
def is_all_white(line):
return re.match(r"^\s*$", line) is not None
def get_indent(line):
return re.match(r"^(\s*)", line).group()
def get_comment_header(line):
m = re.match(r"^(\s*#*)", line)
if m is None: return ""
return m.group(1)

View File

@ -1,38 +0,0 @@
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

View File

@ -1,132 +0,0 @@
import os
import fnmatch
import sys
from Tkinter import *
import SearchEngine
from SearchDialogBase import SearchDialogBase
def grep(text, io=None, flist=None):
root = text._root()
engine = SearchEngine.get(root)
if not hasattr(engine, "_grepdialog"):
engine._grepdialog = GrepDialog(root, engine, flist)
dialog = engine._grepdialog
dialog.open(io)
class GrepDialog(SearchDialogBase):
title = "Find in Files Dialog"
icon = "Grep"
needwrapbutton = 0
def __init__(self, root, engine, flist):
SearchDialogBase.__init__(self, root, engine)
self.flist = flist
self.globvar = StringVar(root)
self.recvar = BooleanVar(root)
def open(self, io=None):
SearchDialogBase.open(self, None)
if io:
path = io.filename or ""
else:
path = ""
dir, base = os.path.split(path)
head, tail = os.path.splitext(base)
if not tail:
tail = ".py"
self.globvar.set(os.path.join(dir, "*" + tail))
def create_entries(self):
SearchDialogBase.create_entries(self)
self.globent = self.make_entry("In files:", self.globvar)
def create_other_buttons(self):
f = self.make_frame()
btn = Checkbutton(f, anchor="w",
variable=self.recvar,
text="Recurse down subdirectories")
btn.pack(side="top", fill="both")
btn.select()
def create_command_buttons(self):
SearchDialogBase.create_command_buttons(self)
self.make_button("Search Files", self.default_command, 1)
def default_command(self, event=None):
prog = self.engine.getprog()
if not prog:
return
path = self.globvar.get()
if not path:
self.top.bell()
return
from OutputWindow import OutputWindow
save = sys.stdout
try:
sys.stdout = OutputWindow(self.flist)
self.grep_it(prog, path)
finally:
sys.stdout = save
def grep_it(self, prog, path):
dir, base = os.path.split(path)
list = self.findfiles(dir, base, self.recvar.get())
list.sort()
self.close()
pat = self.engine.getpat()
print "Searching %s in %s ..." % (`pat`, path)
hits = 0
for fn in list:
try:
f = open(fn)
except IOError, msg:
print msg
continue
lineno = 0
while 1:
block = f.readlines(100000)
if not block:
break
for line in block:
lineno = lineno + 1
if line[-1:] == '\n':
line = line[:-1]
if prog.search(line):
sys.stdout.write("%s: %s: %s\n" % (fn, lineno, line))
hits = hits + 1
if hits:
if hits == 1:
s = ""
else:
s = "s"
print "Found", hits, "hit%s." % s
print "(Hint: right-click to open locations.)"
else:
print "No hits."
def findfiles(self, dir, base, rec):
try:
names = os.listdir(dir or os.curdir)
except os.error, msg:
print msg
return []
list = []
subdirs = []
for name in names:
fn = os.path.join(dir, name)
if os.path.isdir(fn):
subdirs.append(fn)
else:
if fnmatch.fnmatch(name, base):
list.append(fn)
if rec:
for subdir in subdirs:
list.extend(self.findfiles(subdir, base, rec))
return list
def close(self, event=None):
if self.top:
self.top.grab_release()
self.top.withdraw()

View File

@ -1,448 +0,0 @@
import os
import types
import sys
import codecs
import re
import tempfile
import tkFileDialog
import tkMessageBox
from IdleConf import idleconf
#$ event <<open-window-from-file>>
#$ win <Control-o>
#$ unix <Control-x><Control-f>
#$ event <<save-window>>
#$ win <Control-s>
#$ unix <Control-x><Control-s>
#$ event <<save-window-as-file>>
#$ win <Alt-s>
#$ unix <Control-x><Control-w>
#$ event <<save-copy-of-window-as-file>>
#$ win <Alt-Shift-s>
#$ unix <Control-x><w>
#$ event <<print-window>>
#$ win <Control-p>
#$ unix <Control-x><Control-p>
try:
from codecs import BOM_UTF8
except ImportError:
# only available since Python 2.3
BOM_UTF8 = '\xef\xbb\xbf'
# Try setting the locale, so that we can find out
# what encoding to use
try:
import locale
locale.setlocale(locale.LC_CTYPE, "")
except ImportError:
pass
encoding = "ascii"
if sys.platform == 'win32':
# On Windows, we could use "mbcs". However, to give the user
# a portable encoding name, we need to find the code page
try:
encoding = locale.getdefaultlocale()[1]
codecs.lookup(encoding)
except LookupError:
pass
else:
try:
# Different things can fail here: the locale module may not be
# loaded, it may not offer nl_langinfo, or CODESET, or the
# resulting codeset may be unknown to Python. We ignore all
# these problems, falling back to ASCII
encoding = locale.nl_langinfo(locale.CODESET)
codecs.lookup(encoding)
except (NameError, AttributeError, LookupError):
# Try getdefaultlocale well: it parses environment variables,
# which may give a clue. Unfortunately, getdefaultlocale has
# bugs that can cause ValueError.
try:
encoding = locale.getdefaultlocale()[1]
codecs.lookup(encoding)
except (ValueError, LookupError):
pass
encoding = encoding.lower()
coding_re = re.compile("coding[:=]\s*([-\w_.]+)")
def coding_spec(str):
"""Return the encoding declaration according to PEP 263.
Raise LookupError if the encoding is declared but unknown."""
# Only consider the first two lines
str = str.split("\n")[:2]
str = "\n".join(str)
match = coding_re.search(str)
if not match:
return None
name = match.group(1)
# Check whether the encoding is known
import codecs
try:
codecs.lookup(name)
except LookupError:
# The standard encoding error does not indicate the encoding
raise LookupError, "Unknown encoding "+name
return name
class IOBinding:
def __init__(self, editwin):
self.editwin = editwin
self.text = editwin.text
self.__id_open = self.text.bind("<<open-window-from-file>>", self.open)
self.__id_save = self.text.bind("<<save-window>>", self.save)
self.__id_saveas = self.text.bind("<<save-window-as-file>>",
self.save_as)
self.__id_savecopy = self.text.bind("<<save-copy-of-window-as-file>>",
self.save_a_copy)
self.__id_print = self.text.bind("<<print-window>>", self.print_window)
self.fileencoding = None
def close(self):
# Undo command bindings
self.text.unbind("<<open-window-from-file>>", self.__id_open)
self.text.unbind("<<save-window>>", self.__id_save)
self.text.unbind("<<save-window-as-file>>",self.__id_saveas)
self.text.unbind("<<save-copy-of-window-as-file>>", self.__id_savecopy)
self.text.unbind("<<print-window>>", self.__id_print)
# Break cycles
self.editwin = None
self.text = None
self.filename_change_hook = None
def get_saved(self):
return self.editwin.get_saved()
def set_saved(self, flag):
self.editwin.set_saved(flag)
def reset_undo(self):
self.editwin.reset_undo()
filename_change_hook = None
def set_filename_change_hook(self, hook):
self.filename_change_hook = hook
filename = None
def set_filename(self, filename):
self.filename = filename
self.set_saved(1)
if self.filename_change_hook:
self.filename_change_hook()
def open(self, event):
if self.editwin.flist:
filename = self.askopenfile()
if filename:
self.editwin.flist.open(filename)
else:
self.text.focus_set()
return "break"
# Code for use outside IDLE:
if self.get_saved():
reply = self.maybesave()
if reply == "cancel":
self.text.focus_set()
return "break"
filename = self.askopenfile()
if filename:
self.loadfile(filename)
else:
self.text.focus_set()
return "break"
def loadfile(self, filename):
try:
f = open(filename)
chars = f.read()
f.close()
except IOError, msg:
tkMessageBox.showerror("I/O Error", str(msg), master=self.text)
return False
chars = self.decode(chars)
self.text.delete("1.0", "end")
self.set_filename(None)
self.text.insert("1.0", chars)
self.reset_undo()
self.set_filename(filename)
self.text.mark_set("insert", "1.0")
self.text.see("insert")
return True
def decode(self, chars):
# Try to create a Unicode string. If that fails, let Tcl try
# its best
# Check presence of a UTF-8 signature first
if chars.startswith(BOM_UTF8):
try:
chars = chars[3:].decode("utf-8")
except UnicodeError:
# has UTF-8 signature, but fails to decode...
return chars
else:
# Indicates that this file originally had a BOM
self.fileencoding = BOM_UTF8
return chars
# Next look for coding specification
try:
enc = coding_spec(chars)
except LookupError, name:
tkMessageBox.showerror(
title="Error loading the file",
message="The encoding '%s' is not known to this Python "\
"installation. The file may not display correctly" % name,
master = self.text)
enc = None
if enc:
try:
return unicode(chars, enc)
except UnicodeError:
pass
# If it is ASCII, we need not to record anything
try:
return unicode(chars, 'ascii')
except UnicodeError:
pass
# Finally, try the locale's encoding. This is deprecated;
# the user should declare a non-ASCII encoding
try:
chars = unicode(chars, encoding)
self.fileencoding = encoding
except UnicodeError:
pass
return chars
def maybesave(self):
if self.get_saved():
return "yes"
message = "Do you want to save %s before closing?" % (
self.filename or "this untitled document")
m = tkMessageBox.Message(
title="Save On Close",
message=message,
icon=tkMessageBox.QUESTION,
type=tkMessageBox.YESNOCANCEL,
master=self.text)
reply = m.show()
if reply == "yes":
self.save(None)
if not self.get_saved():
reply = "cancel"
self.text.focus_set()
return reply
def save(self, event):
if not self.filename:
self.save_as(event)
else:
if self.writefile(self.filename):
self.set_saved(1)
self.text.focus_set()
return "break"
def save_as(self, event):
filename = self.asksavefile()
if filename:
if self.writefile(filename):
self.set_filename(filename)
self.set_saved(1)
self.text.focus_set()
return "break"
def save_a_copy(self, event):
filename = self.asksavefile()
if filename:
self.writefile(filename)
self.text.focus_set()
return "break"
def print_window(self, event):
if self.get_saved():
filename = self.filename
else:
(tfd, tfn) = tempfile.mkstemp()
os.close(tfd)
filename = tfn
if not self.writefile(filename):
os.unlink(tfn)
return "break"
edconf = idleconf.getsection('EditorWindow')
command = edconf.get('print-command')
command = command % filename
if os.name == 'posix':
command = command + " 2>&1"
pipe = os.popen(command, "r")
output = pipe.read().strip()
status = pipe.close()
if status:
output = "Printing failed (exit status 0x%x)\n" % status + output
if output:
output = "Printing command: %s\n" % repr(command) + output
tkMessageBox.showerror("Print status", output, master=self.text)
return "break"
def writefile(self, filename):
self.fixlastline()
chars = self.encode(self.text.get("1.0", "end-1c"))
try:
f = open(filename, "w")
f.write(chars)
f.close()
## print "saved to", `filename`
return True
except IOError, msg:
tkMessageBox.showerror("I/O Error", str(msg),
master=self.text)
return False
def encode(self, chars):
if isinstance(chars, types.StringType):
# This is either plain ASCII, or Tk was returning mixed-encoding
# text to us. Don't try to guess further.
return chars
# See whether there is anything non-ASCII in it.
# If not, no need to figure out the encoding.
try:
return chars.encode('ascii')
except UnicodeError:
pass
# If there is an encoding declared, try this first.
try:
enc = coding_spec(chars)
failed = None
except LookupError, msg:
failed = msg
enc = None
if enc:
try:
return chars.encode(enc)
except UnicodeError:
failed = "Invalid encoding '%s'" % enc
if failed:
tkMessageBox.showerror(
"I/O Error",
"%s. Saving as UTF-8" % failed,
master = self.text)
# If there was a UTF-8 signature, use that. This should not fail
if self.fileencoding == BOM_UTF8 or failed:
return BOM_UTF8 + chars.encode("utf-8")
# Try the original file encoding next, if any
if self.fileencoding:
try:
return chars.encode(self.fileencoding)
except UnicodeError:
tkMessageBox.showerror(
"I/O Error",
"Cannot save this as '%s' anymore. Saving as UTF-8" % self.fileencoding,
master = self.text)
return BOM_UTF8 + chars.encode("utf-8")
# Nothing was declared, and we had not determined an encoding
# on loading. Recommend an encoding line.
try:
chars = chars.encode(encoding)
enc = encoding
except UnicodeError:
chars = BOM_UTF8 + chars.encode("utf-8")
enc = "utf-8"
tkMessageBox.showerror(
"I/O Error",
"Non-ASCII found, yet no encoding declared. Add a line like\n"
"# -*- coding: %s -*- \nto your file" % enc,
master = self.text)
return chars
def fixlastline(self):
c = self.text.get("end-2c")
if c != '\n':
self.text.insert("end-1c", "\n")
opendialog = None
savedialog = None
filetypes = [
("Python and text files", "*.py *.pyw *.txt", "TEXT"),
("All text files", "*", "TEXT"),
("All files", "*"),
]
def askopenfile(self):
dir, base = self.defaultfilename("open")
if not self.opendialog:
self.opendialog = tkFileDialog.Open(master=self.text,
filetypes=self.filetypes)
return self.opendialog.show(initialdir=dir, initialfile=base)
def defaultfilename(self, mode="open"):
if self.filename:
return os.path.split(self.filename)
else:
try:
pwd = os.getcwd()
except os.error:
pwd = ""
return pwd, ""
def asksavefile(self):
dir, base = self.defaultfilename("save")
if not self.savedialog:
self.savedialog = tkFileDialog.SaveAs(master=self.text,
filetypes=self.filetypes)
return self.savedialog.show(initialdir=dir, initialfile=base)
def test():
root = Tk()
class MyEditWin:
def __init__(self, text):
self.text = text
self.flist = None
self.text.bind("<Control-o>", self.open)
self.text.bind("<Control-s>", self.save)
self.text.bind("<Alt-s>", self.save_as)
self.text.bind("<Alt-z>", self.save_a_copy)
def get_saved(self): return 0
def set_saved(self, flag): pass
def reset_undo(self): pass
def open(self, event):
self.text.event_generate("<<open-window-from-file>>")
def save(self, event):
self.text.event_generate("<<save-window>>")
def save_as(self, event):
self.text.event_generate("<<save-window-as-file>>")
def save_a_copy(self, event):
self.text.event_generate("<<save-copy-of-window-as-file>>")
text = Text(root)
text.pack()
text.focus_set()
editwin = MyEditWin(text)
io = IOBinding(editwin)
root.mainloop()
if __name__ == "__main__":
from Tkinter import *
test()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 B

View File

@ -1,113 +0,0 @@
"""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__))

View File

@ -1,86 +0,0 @@
class History:
def __init__(self, text, output_sep = "\n"):
self.text = text
self.history = []
self.history_prefix = None
self.history_pointer = None
self.output_sep = output_sep
text.bind("<<history-previous>>", self.history_prev)
text.bind("<<history-next>>", self.history_next)
def history_next(self, event):
self.history_do(0)
return "break"
def history_prev(self, event):
self.history_do(1)
return "break"
def _get_source(self, start, end):
# Get source code from start index to end index. Lines in the
# text control may be separated by sys.ps2 .
lines = self.text.get(start, end).split(self.output_sep)
return "\n".join(lines)
def _put_source(self, where, source):
output = self.output_sep.join(source.split("\n"))
self.text.insert(where, output)
def history_do(self, reverse):
nhist = len(self.history)
pointer = self.history_pointer
prefix = self.history_prefix
if pointer is not None and prefix is not None:
if self.text.compare("insert", "!=", "end-1c") or \
self._get_source("iomark", "end-1c") != self.history[pointer]:
pointer = prefix = None
if pointer is None or prefix is None:
prefix = self._get_source("iomark", "end-1c")
if reverse:
pointer = nhist
else:
pointer = -1
nprefix = len(prefix)
while 1:
if reverse:
pointer = pointer - 1
else:
pointer = pointer + 1
if pointer < 0 or pointer >= nhist:
self.text.bell()
if self._get_source("iomark", "end-1c") != prefix:
self.text.delete("iomark", "end-1c")
self._put_source("iomark", prefix)
pointer = prefix = None
break
item = self.history[pointer]
if item[:nprefix] == prefix and len(item) > nprefix:
self.text.delete("iomark", "end-1c")
self._put_source("iomark", item)
break
self.text.mark_set("insert", "end-1c")
self.text.see("insert")
self.text.tag_remove("sel", "1.0", "end")
self.history_pointer = pointer
self.history_prefix = prefix
def history_store(self, source):
source = source.strip()
if len(source) > 2:
# avoid duplicates
try:
self.history.remove(source)
except ValueError:
pass
self.history.append(source)
self.history_pointer = None
self.history_prefix = None
def recall(self, s):
s = s.strip()
self.text.tag_remove("sel", "1.0", "end")
self.text.delete("iomark", "end-1c")
self.text.mark_set("insert", "end-1c")
self.text.insert("insert", s)
self.text.see("insert")

View File

@ -1,4 +0,0 @@
include *.txt
include idle
include MANIFEST.in
include MANIFEST

View File

@ -1,137 +0,0 @@
# 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()

View File

@ -1,32 +0,0 @@
from Tkinter import *
class MultiStatusBar(Frame):
def __init__(self, master=None, **kw):
if master is None:
master = Tk()
apply(Frame.__init__, (self, master), kw)
self.labels = {}
def set_label(self, name, text='', side=LEFT):
if not self.labels.has_key(name):
label = Label(self, bd=1, relief=SUNKEN, anchor=W)
label.pack(side=side)
self.labels[name] = label
else:
label = self.labels[name]
label.config(text=text)
def _test():
b = Frame()
c = Text(b)
c.pack(side=TOP)
a = MultiStatusBar(b)
a.set_label("one", "hello")
a.set_label("two", "world")
a.pack(side=BOTTOM, fill=X)
b.pack()
b.mainloop()
if __name__ == '__main__':
_test()

View File

@ -1,130 +0,0 @@
(For a more detailed change log, see the file ChangeLog.)
----------------------------------------------------------------------
New in IDLE 0.5 (2/15/2000)
-------------------------
Tons of stuff, much of it contributed by Tim Peters and Mark Hammond:
- Status bar, displaying current line/column (Moshe Zadka).
- Better stack viewer, using tree widget. (XXX Only used by Stack
Viewer menu, not by the debugger.)
- Format paragraph now recognizes Python block comments and reformats
them correctly (MH)
- New version of pyclbr.py parses top-level functions and understands
much more of Python's syntax; this is reflected in the class and path
browsers (TP)
- Much better auto-indent; knows how to indent the insides of
multi-line statements (TP)
- Call tip window pops up when you type the name of a known function
followed by an open parenthesis. Hit ESC or click elsewhere in the
window to close the tip window (MH)
- Comment out region now inserts ## to make it stand out more (TP)
- New path and class browsers based on a tree widget that looks
familiar to Windows users
- Reworked script running commands to be more intuitive: I/O now
always goes to the *Python Shell* window, and raw_input() works
correctly. You use F5 to import/reload a module: this adds the module
name to the __main__ namespace. You use Control-F5 to run a script:
this runs the script *in* the __main__ namespace. The latter also
sets sys.argv[] to the script name
New in IDLE 0.4 (4/7/99)
------------------------
Most important change: a new menu entry "File -> Path browser", shows
a 4-column hierarchical browser which lets you browse sys.path,
directories, modules, and classes. Yes, it's a superset of the Class
browser menu entry. There's also a new internal module,
MultiScrolledLists.py, which provides the framework for this dialog.
New in IDLE 0.3 (2/17/99)
-------------------------
Most important changes:
- Enabled support for running a module, with or without the debugger.
Output goes to a new window. Pressing F5 in a module is effectively a
reload of that module; Control-F5 loads it under the debugger.
- Re-enable tearing off the Windows menu, and make a torn-off Windows
menu update itself whenever a window is opened or closed.
- Menu items can now be have a checkbox (when the menu label starts
with "!"); use this for the Debugger and "Auto-open stack viewer"
(was: JIT stack viewer) menu items.
- Added a Quit button to the Debugger API.
- The current directory is explicitly inserted into sys.path.
- Fix the debugger (when using Python 1.5.2b2) to use canonical
filenames for breakpoints, so these actually work. (There's still a
lot of work to be done to the management of breakpoints in the
debugger though.)
- Closing a window that is still colorizing now actually works.
- Allow dragging of the separator between the two list boxes in the
class browser.
- Bind ESC to "close window" of the debugger, stack viewer and class
browser. It removes the selection highlighting in regular text
windows. (These are standard Windows conventions.)
----------------------------------------------------------------------
New in IDLE 0.2 (1/8/99)
------------------------
Lots of changes; here are the highlights:
General:
- You can now write and configure your own IDLE extension modules; see
extend.txt.
File menu:
The command to open the Python shell window is now in the File menu.
Edit menu:
New Find dialog with more options; replace dialog; find in files dialog.
Commands to tabify or untabify a region.
Command to format a paragraph.
Debug menu:
JIT (Just-In-Time) stack viewer toggle -- if set, the stack viewer
automaticall pops up when you get a traceback.
Windows menu:
Zoom height -- make the window full height.
Help menu:
The help text now show up in a regular window so you can search and
even edit it if you like.
----------------------------------------------------------------------
IDLE 0.1 was distributed with the Python 1.5.2b1 release on 12/22/98.
======================================================================

View File

@ -1,151 +0,0 @@
# XXX TO DO:
# - popup menu
# - support partial or total redisplay
# - more doc strings
# - tooltips
# object browser
# XXX TO DO:
# - for classes/modules, add "open source" to object browser
from TreeWidget import TreeItem, TreeNode, ScrolledCanvas
from repr import Repr
myrepr = Repr()
myrepr.maxstring = 100
myrepr.maxother = 100
class ObjectTreeItem(TreeItem):
def __init__(self, labeltext, object, setfunction=None):
self.labeltext = labeltext
self.object = object
self.setfunction = setfunction
def GetLabelText(self):
return self.labeltext
def GetText(self):
return myrepr.repr(self.object)
def GetIconName(self):
if not self.IsExpandable():
return "python"
def IsEditable(self):
return self.setfunction is not None
def SetText(self, text):
try:
value = eval(text)
self.setfunction(value)
except:
pass
else:
self.object = value
def IsExpandable(self):
return not not dir(self.object)
def GetSubList(self):
keys = dir(self.object)
sublist = []
for key in keys:
try:
value = getattr(self.object, key)
except AttributeError:
continue
item = make_objecttreeitem(
str(key) + " =",
value,
lambda value, key=key, object=self.object:
setattr(object, key, value))
sublist.append(item)
return sublist
class InstanceTreeItem(ObjectTreeItem):
def IsExpandable(self):
return True
def GetSubList(self):
sublist = ObjectTreeItem.GetSubList(self)
sublist.insert(0,
make_objecttreeitem("__class__ =", self.object.__class__))
return sublist
class ClassTreeItem(ObjectTreeItem):
def IsExpandable(self):
return True
def GetSubList(self):
sublist = ObjectTreeItem.GetSubList(self)
if len(self.object.__bases__) == 1:
item = make_objecttreeitem("__bases__[0] =",
self.object.__bases__[0])
else:
item = make_objecttreeitem("__bases__ =", self.object.__bases__)
sublist.insert(0, item)
return sublist
class AtomicObjectTreeItem(ObjectTreeItem):
def IsExpandable(self):
return 0
class SequenceTreeItem(ObjectTreeItem):
def IsExpandable(self):
return len(self.object) > 0
def keys(self):
return range(len(self.object))
def GetSubList(self):
sublist = []
for key in self.keys():
try:
value = self.object[key]
except KeyError:
continue
def setfunction(value, key=key, object=self.object):
object[key] = value
item = make_objecttreeitem(`key` + ":", value, setfunction)
sublist.append(item)
return sublist
class DictTreeItem(SequenceTreeItem):
def keys(self):
keys = self.object.keys()
try:
keys.sort()
except:
pass
return keys
from types import *
dispatch = {
IntType: AtomicObjectTreeItem,
LongType: AtomicObjectTreeItem,
FloatType: AtomicObjectTreeItem,
StringType: AtomicObjectTreeItem,
TupleType: SequenceTreeItem,
ListType: SequenceTreeItem,
DictType: DictTreeItem,
InstanceType: InstanceTreeItem,
ClassType: ClassTreeItem,
}
def make_objecttreeitem(labeltext, object, setfunction=None):
t = type(object)
if dispatch.has_key(t):
c = dispatch[t]
else:
c = ObjectTreeItem
return c(labeltext, object, setfunction)
# Test script
def _test():
import sys
from Tkinter import Tk
root = Tk()
root.configure(bd=0, bg="yellow")
root.focus_set()
sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1)
sc.frame.pack(expand=1, fill="both")
item = make_objecttreeitem("sys", sys)
node = TreeNode(sc.canvas, None, item)
node.update()
root.mainloop()
if __name__ == '__main__':
_test()

View File

@ -1,275 +0,0 @@
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()

View File

@ -1,157 +0,0 @@
from Tkinter import *
from EditorWindow import EditorWindow
import re
import tkMessageBox
import IOBinding
class OutputWindow(EditorWindow):
"""An editor window that can serve as an output file.
Also the future base class for the Python shell window.
This class has no input facilities.
"""
def __init__(self, *args):
apply(EditorWindow.__init__, (self,) + args)
self.text.bind("<<goto-file-line>>", self.goto_file_line)
# Customize EditorWindow
def ispythonsource(self, filename):
# No colorization needed
return 0
def short_title(self):
return "Output"
def maybesave(self):
# Override base class method -- don't ask any questions
if self.get_saved():
return "yes"
else:
return "no"
# Act as output file
def write(self, s, tags=(), mark="insert"):
# Tk assumes that byte strings are Latin-1;
# we assume that they are in the locale's encoding
if isinstance(s, str):
try:
s = unicode(s, IOBinding.encoding)
except UnicodeError:
# some other encoding; let Tcl deal with it
pass
self.text.insert(mark, s, tags)
self.text.see(mark)
self.text.update()
def writelines(self, l):
map(self.write, l)
def flush(self):
pass
# Our own right-button menu
rmenu_specs = [
("Go to file/line", "<<goto-file-line>>"),
]
file_line_pats = [
r'file "([^"]*)", line (\d+)',
r'([^\s]+)\((\d+)\)',
r'([^\s]+):\s*(\d+):',
]
file_line_progs = None
def goto_file_line(self, event=None):
if self.file_line_progs is None:
l = []
for pat in self.file_line_pats:
l.append(re.compile(pat, re.IGNORECASE))
self.file_line_progs = l
# x, y = self.event.x, self.event.y
# self.text.mark_set("insert", "@%d,%d" % (x, y))
line = self.text.get("insert linestart", "insert lineend")
result = self._file_line_helper(line)
if not result:
# Try the previous line. This is handy e.g. in tracebacks,
# where you tend to right-click on the displayed source line
line = self.text.get("insert -1line linestart",
"insert -1line lineend")
result = self._file_line_helper(line)
if not result:
tkMessageBox.showerror(
"No special line",
"The line you point at doesn't look like "
"a valid file name followed by a line number.",
master=self.text)
return
filename, lineno = result
edit = self.flist.open(filename)
edit.gotoline(lineno)
def _file_line_helper(self, line):
for prog in self.file_line_progs:
m = prog.search(line)
if m:
break
else:
return None
filename, lineno = m.group(1, 2)
try:
f = open(filename, "r")
f.close()
except IOError:
return None
try:
return filename, int(lineno)
except TypeError:
return None
# These classes are currently not used but might come in handy
class OnDemandOutputWindow:
tagdefs = {
# XXX Should use IdlePrefs.ColorPrefs
"stdout": {"foreground": "blue"},
"stderr": {"foreground": "#007700"},
}
def __init__(self, flist):
self.flist = flist
self.owin = None
def write(self, s, tags, mark):
if not self.owin:
self.setup()
self.owin.write(s, tags, mark)
def setup(self):
self.owin = owin = OutputWindow(self.flist)
text = owin.text
for tag, cnf in self.tagdefs.items():
if cnf:
apply(text.tag_configure, (tag,), cnf)
text.tag_raise('sel')
self.write = self.owin.write
class PseudoFile:
def __init__(self, owin, tags, mark="end"):
self.owin = owin
self.tags = tags
self.mark = mark
def write(self, s):
self.owin.write(s, self.tags, self.mark)
def writelines(self, l):
map(self.write, l)
def flush(self):
pass

View File

@ -1,189 +0,0 @@
"""ParenMatch -- An IDLE extension for parenthesis matching.
When you hit a right paren, the cursor should move briefly to the left
paren. Paren here is used generically; the matching applies to
parentheses, square brackets, and curly braces.
WARNING: This extension will fight with the CallTips extension,
because they both are interested in the KeyRelease-parenright event.
We'll have to fix IDLE to do something reasonable when two or more
extensions what to capture the same event.
"""
import PyParse
from AutoIndent import AutoIndent, index2line
from IdleConf import idleconf
class ParenMatch:
"""Highlight matching parentheses
There are three supported style of paren matching, based loosely
on the Emacs options. The style is select based on the
HILITE_STYLE attribute; it can be changed used the set_style
method.
The supported styles are:
default -- When a right paren is typed, highlight the matching
left paren for 1/2 sec.
expression -- When a right paren is typed, highlight the entire
expression from the left paren to the right paren.
TODO:
- fix interaction with CallTips
- extend IDLE with configuration dialog to change options
- implement rest of Emacs highlight styles (see below)
- print mismatch warning in IDLE status window
Note: In Emacs, there are several styles of highlight where the
matching paren is highlighted whenever the cursor is immediately
to the right of a right paren. I don't know how to do that in Tk,
so I haven't bothered.
"""
menudefs = []
keydefs = {
'<<flash-open-paren>>' : ('<KeyRelease-parenright>',
'<KeyRelease-bracketright>',
'<KeyRelease-braceright>'),
'<<check-restore>>' : ('<KeyPress>',),
}
windows_keydefs = {}
unix_keydefs = {}
iconf = idleconf.getsection('ParenMatch')
STYLE = iconf.getdef('style', 'default')
FLASH_DELAY = iconf.getint('flash-delay')
HILITE_CONFIG = iconf.getcolor('hilite')
BELL = iconf.getboolean('bell')
del iconf
def __init__(self, editwin):
self.editwin = editwin
self.text = editwin.text
self.finder = LastOpenBracketFinder(editwin)
self.counter = 0
self._restore = None
self.set_style(self.STYLE)
def set_style(self, style):
self.STYLE = style
if style == "default":
self.create_tag = self.create_tag_default
self.set_timeout = self.set_timeout_last
elif style == "expression":
self.create_tag = self.create_tag_expression
self.set_timeout = self.set_timeout_none
def flash_open_paren_event(self, event):
index = self.finder.find(keysym_type(event.keysym))
if index is None:
self.warn_mismatched()
return
self._restore = 1
self.create_tag(index)
self.set_timeout()
def check_restore_event(self, event=None):
if self._restore:
self.text.tag_delete("paren")
self._restore = None
def handle_restore_timer(self, timer_count):
if timer_count + 1 == self.counter:
self.check_restore_event()
def warn_mismatched(self):
if self.BELL:
self.text.bell()
# any one of the create_tag_XXX methods can be used depending on
# the style
def create_tag_default(self, index):
"""Highlight the single paren that matches"""
self.text.tag_add("paren", index)
self.text.tag_config("paren", self.HILITE_CONFIG)
def create_tag_expression(self, index):
"""Highlight the entire expression"""
self.text.tag_add("paren", index, "insert")
self.text.tag_config("paren", self.HILITE_CONFIG)
# any one of the set_timeout_XXX methods can be used depending on
# the style
def set_timeout_none(self):
"""Highlight will remain until user input turns it off"""
pass
def set_timeout_last(self):
"""The last highlight created will be removed after .5 sec"""
# associate a counter with an event; only disable the "paren"
# tag if the event is for the most recent timer.
self.editwin.text_frame.after(self.FLASH_DELAY,
lambda self=self, c=self.counter: \
self.handle_restore_timer(c))
self.counter = self.counter + 1
def keysym_type(ks):
# Not all possible chars or keysyms are checked because of the
# limited context in which the function is used.
if ks == "parenright" or ks == "(":
return "paren"
if ks == "bracketright" or ks == "[":
return "bracket"
if ks == "braceright" or ks == "{":
return "brace"
class LastOpenBracketFinder:
num_context_lines = AutoIndent.num_context_lines
indentwidth = AutoIndent.indentwidth
tabwidth = AutoIndent.tabwidth
context_use_ps1 = AutoIndent.context_use_ps1
def __init__(self, editwin):
self.editwin = editwin
self.text = editwin.text
def _find_offset_in_buf(self, lno):
y = PyParse.Parser(self.indentwidth, self.tabwidth)
for context in self.num_context_lines:
startat = max(lno - context, 1)
startatindex = `startat` + ".0"
# rawtext needs to contain everything up to the last
# character, which was the close paren. the parser also
# requires that the last line ends with "\n"
rawtext = self.text.get(startatindex, "insert")[:-1] + "\n"
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)
i = y.get_last_open_bracket_pos()
return i, y.str
def find(self, right_keysym_type):
"""Return the location of the last open paren"""
lno = index2line(self.text.index("insert"))
i, buf = self._find_offset_in_buf(lno)
if i is None \
or keysym_type(buf[i]) != right_keysym_type:
return None
lines_back = buf[i:].count("\n") - 1
# subtract one for the "\n" added to please the parser
upto_open = buf[:i]
j = upto_open.rfind("\n") + 1 # offset of column 0 of line
offset = i - j
return "%d.%d" % (lno - lines_back, 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

View File

@ -1,95 +0,0 @@
import os
import sys
import imp
from TreeWidget import TreeItem
from ClassBrowser import ClassBrowser, ModuleBrowserTreeItem
class PathBrowser(ClassBrowser):
def __init__(self, flist):
self.init(flist)
def settitle(self):
self.top.wm_title("Path Browser")
self.top.wm_iconname("Path Browser")
def rootnode(self):
return PathBrowserTreeItem()
class PathBrowserTreeItem(TreeItem):
def GetText(self):
return "sys.path"
def GetSubList(self):
sublist = []
for dir in sys.path:
item = DirBrowserTreeItem(dir)
sublist.append(item)
return sublist
class DirBrowserTreeItem(TreeItem):
def __init__(self, dir, packages=[]):
self.dir = dir
self.packages = packages
def GetText(self):
if not self.packages:
return self.dir
else:
return self.packages[-1] + ": package"
def GetSubList(self):
try:
names = os.listdir(self.dir or os.curdir)
except os.error:
return []
packages = []
for name in names:
file = os.path.join(self.dir, name)
if self.ispackagedir(file):
nn = os.path.normcase(name)
packages.append((nn, name, file))
packages.sort()
sublist = []
for nn, name, file in packages:
item = DirBrowserTreeItem(file, self.packages + [name])
sublist.append(item)
for nn, name in self.listmodules(names):
item = ModuleBrowserTreeItem(os.path.join(self.dir, name))
sublist.append(item)
return sublist
def ispackagedir(self, file):
if not os.path.isdir(file):
return 0
init = os.path.join(file, "__init__.py")
return os.path.exists(init)
def listmodules(self, allnames):
modules = {}
suffixes = imp.get_suffixes()
sorted = []
for suff, mode, flag in suffixes:
i = -len(suff)
for name in allnames[:]:
normed_name = os.path.normcase(name)
if normed_name[i:] == suff:
mod_name = name[:i]
if not modules.has_key(mod_name):
modules[mod_name] = None
sorted.append((normed_name, name))
allnames.remove(name)
sorted.sort()
return sorted
def main():
import PyShell
PathBrowser(PyShell.flist)
if sys.stdin is sys.__stdin__:
mainloop()
if __name__ == "__main__":
main()

View File

@ -1,85 +0,0 @@
from WidgetRedirector import WidgetRedirector
from Delegator import Delegator
class Percolator:
def __init__(self, text):
# XXX would be nice to inherit from Delegator
self.text = text
self.redir = WidgetRedirector(text)
self.top = self.bottom = Delegator(text)
self.bottom.insert = self.redir.register("insert", self.insert)
self.bottom.delete = self.redir.register("delete", self.delete)
self.filters = []
def close(self):
while self.top is not self.bottom:
self.removefilter(self.top)
self.top = None
self.bottom.setdelegate(None); self.bottom = None
self.redir.close(); self.redir = None
self.text = None
def insert(self, index, chars, tags=None):
# Could go away if inheriting from Delegator
self.top.insert(index, chars, tags)
def delete(self, index1, index2=None):
# Could go away if inheriting from Delegator
self.top.delete(index1, index2)
def insertfilter(self, filter):
# Perhaps rename to pushfilter()?
assert isinstance(filter, Delegator)
assert filter.delegate is None
filter.setdelegate(self.top)
self.top = filter
def removefilter(self, filter):
# XXX Perhaps should only support popfilter()?
assert isinstance(filter, Delegator)
assert filter.delegate is not None
f = self.top
if f is filter:
self.top = filter.delegate
filter.setdelegate(None)
else:
while f.delegate is not filter:
assert f is not self.bottom
f.resetcache()
f = f.delegate
f.setdelegate(filter.delegate)
filter.setdelegate(None)
def main():
class Tracer(Delegator):
def __init__(self, name):
self.name = name
Delegator.__init__(self, None)
def insert(self, *args):
print self.name, ": insert", args
apply(self.delegate.insert, args)
def delete(self, *args):
print self.name, ": delete", args
apply(self.delegate.delete, args)
root = Tk()
root.wm_protocol("WM_DELETE_WINDOW", root.quit)
text = Text()
text.pack()
text.focus_set()
p = Percolator(text)
t1 = Tracer("t1")
t2 = Tracer("t2")
p.insertfilter(t1)
p.insertfilter(t2)
root.mainloop()
p.removefilter(t2)
root.mainloop()
p.insertfilter(t2)
p.removefilter(t1)
root.mainloop()
if __name__ == "__main__":
from Tkinter import *
main()

View File

@ -1,584 +0,0 @@
import re
import sys
# Reason last stmt is continued (or C_NONE if it's not).
C_NONE, C_BACKSLASH, C_STRING, C_BRACKET = range(4)
if 0: # for throwaway debugging output
def dump(*stuff):
sys.__stdout__.write(" ".join(map(str, stuff)) + "\n")
# Find what looks like the start of a popular stmt.
_synchre = re.compile(r"""
^
[ \t]*
(?: if
| for
| while
| else
| def
| return
| assert
| break
| class
| continue
| elif
| try
| except
| raise
| import
| yield
)
\b
""", re.VERBOSE | re.MULTILINE).search
# Match blank line or non-indenting comment line.
_junkre = re.compile(r"""
[ \t]*
(?: \# \S .* )?
\n
""", re.VERBOSE).match
# Match any flavor of string; the terminating quote is optional
# so that we're robust in the face of incomplete program text.
_match_stringre = re.compile(r"""
\""" [^"\\]* (?:
(?: \\. | "(?!"") )
[^"\\]*
)*
(?: \""" )?
| " [^"\\\n]* (?: \\. [^"\\\n]* )* "?
| ''' [^'\\]* (?:
(?: \\. | '(?!'') )
[^'\\]*
)*
(?: ''' )?
| ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""", re.VERBOSE | re.DOTALL).match
# Match a line that starts with something interesting;
# used to find the first item of a bracket structure.
_itemre = re.compile(r"""
[ \t]*
[^\s#\\] # if we match, m.end()-1 is the interesting char
""", re.VERBOSE).match
# Match start of stmts that should be followed by a dedent.
_closere = re.compile(r"""
\s*
(?: return
| break
| continue
| raise
| pass
)
\b
""", re.VERBOSE).match
# Chew up non-special chars as quickly as possible. If match is
# successful, m.end() less 1 is the index of the last boring char
# matched. If match is unsuccessful, the string starts with an
# interesting char.
_chew_ordinaryre = re.compile(r"""
[^[\](){}#'"\\]+
""", re.VERBOSE).match
# Build translation table to map uninteresting chars to "x", open
# brackets to "(", and close brackets to ")".
_tran = ['x'] * 256
for ch in "({[":
_tran[ord(ch)] = '('
for ch in ")}]":
_tran[ord(ch)] = ')'
for ch in "\"'\\\n#":
_tran[ord(ch)] = ch
_tran = ''.join(_tran)
del ch
try:
UnicodeType = type(unicode(""))
except NameError:
UnicodeType = None
class Parser:
def __init__(self, indentwidth, tabwidth):
self.indentwidth = indentwidth
self.tabwidth = tabwidth
def set_str(self, str):
assert len(str) == 0 or str[-1] == '\n'
if type(str) is UnicodeType:
# The parse functions have no idea what to do with Unicode, so
# replace all Unicode characters with "x". This is "safe"
# so long as the only characters germane to parsing the structure
# of Python are 7-bit ASCII. It's *necessary* because Unicode
# strings don't have a .translate() method that supports
# deletechars.
uniphooey = str
str = []
push = str.append
for raw in map(ord, uniphooey):
push(raw < 127 and chr(raw) or "x")
str = "".join(str)
self.str = str
self.study_level = 0
# Return index of a good place to begin parsing, as close to the
# end of the string as possible. This will be the start of some
# popular stmt like "if" or "def". Return None if none found:
# the caller should pass more prior context then, if possible, or
# if not (the entire program text up until the point of interest
# has already been tried) pass 0 to set_lo.
#
# This will be reliable iff given a reliable is_char_in_string
# function, meaning that when it says "no", it's absolutely
# guaranteed that the char is not in a string.
#
# Ack, hack: in the shell window this kills us, because there's
# no way to tell the differences between output, >>> etc and
# user input. Indeed, IDLE's first output line makes the rest
# look like it's in an unclosed paren!:
# Python 1.5.2 (#0, Apr 13 1999, ...
def find_good_parse_start(self, use_ps1, is_char_in_string=None,
_synchre=_synchre):
str, pos = self.str, None
if use_ps1:
# shell window
ps1 = '\n' + sys.ps1
i = str.rfind(ps1)
if i >= 0:
pos = i + len(ps1)
# make it look like there's a newline instead
# of ps1 at the start -- hacking here once avoids
# repeated hackery later
self.str = str[:pos-1] + '\n' + str[pos:]
return pos
# File window -- real work.
if not is_char_in_string:
# no clue -- make the caller pass everything
return None
# Peek back from the end for a good place to start,
# but don't try too often; pos will be left None, or
# bumped to a legitimate synch point.
limit = len(str)
for tries in range(5):
i = str.rfind(":\n", 0, limit)
if i < 0:
break
i = str.rfind('\n', 0, i) + 1 # start of colon line
m = _synchre(str, i, limit)
if m and not is_char_in_string(m.start()):
pos = m.start()
break
limit = i
if pos is None:
# Nothing looks like a block-opener, or stuff does
# but is_char_in_string keeps returning true; most likely
# we're in or near a giant string, the colorizer hasn't
# caught up enough to be helpful, or there simply *aren't*
# any interesting stmts. In any of these cases we're
# going to have to parse the whole thing to be sure, so
# give it one last try from the start, but stop wasting
# time here regardless of the outcome.
m = _synchre(str)
if m and not is_char_in_string(m.start()):
pos = m.start()
return pos
# Peeking back worked; look forward until _synchre no longer
# matches.
i = pos + 1
while 1:
m = _synchre(str, i)
if m:
s, i = m.span()
if not is_char_in_string(s):
pos = s
else:
break
return pos
# Throw away the start of the string. Intended to be called with
# find_good_parse_start's result.
def set_lo(self, lo):
assert lo == 0 or self.str[lo-1] == '\n'
if lo > 0:
self.str = self.str[lo:]
# As quickly as humanly possible <wink>, find the line numbers (0-
# based) of the non-continuation lines.
# Creates self.{goodlines, continuation}.
def _study1(self):
if self.study_level >= 1:
return
self.study_level = 1
# Map all uninteresting characters to "x", all open brackets
# to "(", all close brackets to ")", then collapse runs of
# uninteresting characters. This can cut the number of chars
# by a factor of 10-40, and so greatly speed the following loop.
str = self.str
str = str.translate(_tran)
str = str.replace('xxxxxxxx', 'x')
str = str.replace('xxxx', 'x')
str = str.replace('xx', 'x')
str = str.replace('xx', 'x')
str = str.replace('\nx', '\n')
# note that replacing x\n with \n would be incorrect, because
# x may be preceded by a backslash
# March over the squashed version of the program, accumulating
# the line numbers of non-continued stmts, and determining
# whether & why the last stmt is a continuation.
continuation = C_NONE
level = lno = 0 # level is nesting level; lno is line number
self.goodlines = goodlines = [0]
push_good = goodlines.append
i, n = 0, len(str)
while i < n:
ch = str[i]
i = i+1
# cases are checked in decreasing order of frequency
if ch == 'x':
continue
if ch == '\n':
lno = lno + 1
if level == 0:
push_good(lno)
# else we're in an unclosed bracket structure
continue
if ch == '(':
level = level + 1
continue
if ch == ')':
if level:
level = level - 1
# else the program is invalid, but we can't complain
continue
if ch == '"' or ch == "'":
# consume the string
quote = ch
if str[i-1:i+2] == quote * 3:
quote = quote * 3
w = len(quote) - 1
i = i+w
while i < n:
ch = str[i]
i = i+1
if ch == 'x':
continue
if str[i-1:i+w] == quote:
i = i+w
break
if ch == '\n':
lno = lno + 1
if w == 0:
# unterminated single-quoted string
if level == 0:
push_good(lno)
break
continue
if ch == '\\':
assert i < n
if str[i] == '\n':
lno = lno + 1
i = i+1
continue
# else comment char or paren inside string
else:
# didn't break out of the loop, so we're still
# inside a string
continuation = C_STRING
continue # with outer loop
if ch == '#':
# consume the comment
i = str.find('\n', i)
assert i >= 0
continue
assert ch == '\\'
assert i < n
if str[i] == '\n':
lno = lno + 1
if i+1 == n:
continuation = C_BACKSLASH
i = i+1
# The last stmt may be continued for all 3 reasons.
# String continuation takes precedence over bracket
# continuation, which beats backslash continuation.
if continuation != C_STRING and level > 0:
continuation = C_BRACKET
self.continuation = continuation
# Push the final line number as a sentinel value, regardless of
# whether it's continued.
assert (continuation == C_NONE) == (goodlines[-1] == lno)
if goodlines[-1] != lno:
push_good(lno)
def get_continuation_type(self):
self._study1()
return self.continuation
# study1 was sufficient to determine the continuation status,
# but doing more requires looking at every character. study2
# does this for the last interesting statement in the block.
# Creates:
# self.stmt_start, stmt_end
# slice indices of last interesting stmt
# self.lastch
# last non-whitespace character before optional trailing
# comment
# self.lastopenbracketpos
# if continuation is C_BRACKET, index of last open bracket
def _study2(self):
if self.study_level >= 2:
return
self._study1()
self.study_level = 2
# Set p and q to slice indices of last interesting stmt.
str, goodlines = self.str, self.goodlines
i = len(goodlines) - 1
p = len(str) # index of newest line
while i:
assert p
# p is the index of the stmt at line number goodlines[i].
# Move p back to the stmt at line number goodlines[i-1].
q = p
for nothing in range(goodlines[i-1], goodlines[i]):
# tricky: sets p to 0 if no preceding newline
p = str.rfind('\n', 0, p-1) + 1
# The stmt str[p:q] isn't a continuation, but may be blank
# or a non-indenting comment line.
if _junkre(str, p):
i = i-1
else:
break
if i == 0:
# nothing but junk!
assert p == 0
q = p
self.stmt_start, self.stmt_end = p, q
# Analyze this stmt, to find the last open bracket (if any)
# and last interesting character (if any).
lastch = ""
stack = [] # stack of open bracket indices
push_stack = stack.append
while p < q:
# suck up all except ()[]{}'"#\\
m = _chew_ordinaryre(str, p, q)
if m:
# we skipped at least one boring char
newp = m.end()
# back up over totally boring whitespace
i = newp - 1 # index of last boring char
while i >= p and str[i] in " \t\n":
i = i-1
if i >= p:
lastch = str[i]
p = newp
if p >= q:
break
ch = str[p]
if ch in "([{":
push_stack(p)
lastch = ch
p = p+1
continue
if ch in ")]}":
if stack:
del stack[-1]
lastch = ch
p = p+1
continue
if ch == '"' or ch == "'":
# consume string
# Note that study1 did this with a Python loop, but
# we use a regexp here; the reason is speed in both
# cases; the string may be huge, but study1 pre-squashed
# strings to a couple of characters per line. study1
# also needed to keep track of newlines, and we don't
# have to.
lastch = ch
p = _match_stringre(str, p, q).end()
continue
if ch == '#':
# consume comment and trailing newline
p = str.find('\n', p, q) + 1
assert p > 0
continue
assert ch == '\\'
p = p+1 # beyond backslash
assert p < q
if str[p] != '\n':
# the program is invalid, but can't complain
lastch = ch + str[p]
p = p+1 # beyond escaped char
# end while p < q:
self.lastch = lastch
if stack:
self.lastopenbracketpos = stack[-1]
# Assuming continuation is C_BRACKET, return the number
# of spaces the next line should be indented.
def compute_bracket_indent(self):
self._study2()
assert self.continuation == C_BRACKET
j = self.lastopenbracketpos
str = self.str
n = len(str)
origi = i = str.rfind('\n', 0, j) + 1
j = j+1 # one beyond open bracket
# find first list item; set i to start of its line
while j < n:
m = _itemre(str, j)
if m:
j = m.end() - 1 # index of first interesting char
extra = 0
break
else:
# this line is junk; advance to next line
i = j = str.find('\n', j) + 1
else:
# nothing interesting follows the bracket;
# reproduce the bracket line's indentation + a level
j = i = origi
while str[j] in " \t":
j = j+1
extra = self.indentwidth
return len(str[i:j].expandtabs(self.tabwidth)) + extra
# Return number of physical lines in last stmt (whether or not
# it's an interesting stmt! this is intended to be called when
# continuation is C_BACKSLASH).
def get_num_lines_in_stmt(self):
self._study1()
goodlines = self.goodlines
return goodlines[-1] - goodlines[-2]
# Assuming continuation is C_BACKSLASH, return the number of spaces
# the next line should be indented. Also assuming the new line is
# the first one following the initial line of the stmt.
def compute_backslash_indent(self):
self._study2()
assert self.continuation == C_BACKSLASH
str = self.str
i = self.stmt_start
while str[i] in " \t":
i = i+1
startpos = i
# See whether the initial line starts an assignment stmt; i.e.,
# look for an = operator
endpos = str.find('\n', startpos) + 1
found = level = 0
while i < endpos:
ch = str[i]
if ch in "([{":
level = level + 1
i = i+1
elif ch in ")]}":
if level:
level = level - 1
i = i+1
elif ch == '"' or ch == "'":
i = _match_stringre(str, i, endpos).end()
elif ch == '#':
break
elif level == 0 and ch == '=' and \
(i == 0 or str[i-1] not in "=<>!") and \
str[i+1] != '=':
found = 1
break
else:
i = i+1
if found:
# found a legit =, but it may be the last interesting
# thing on the line
i = i+1 # move beyond the =
found = re.match(r"\s*\\", str[i:endpos]) is None
if not found:
# oh well ... settle for moving beyond the first chunk
# of non-whitespace chars
i = startpos
while str[i] not in " \t\n":
i = i+1
return len(str[self.stmt_start:i].expandtabs(\
self.tabwidth)) + 1
# Return the leading whitespace on the initial line of the last
# interesting stmt.
def get_base_indent_string(self):
self._study2()
i, n = self.stmt_start, self.stmt_end
j = i
str = self.str
while j < n and str[j] in " \t":
j = j + 1
return str[i:j]
# Did the last interesting stmt open a block?
def is_block_opener(self):
self._study2()
return self.lastch == ':'
# Did the last interesting stmt close a block?
def is_block_closer(self):
self._study2()
return _closere(self.str, self.stmt_start) is not None
# index of last open bracket ({[, or None if none
lastopenbracketpos = None
def get_last_open_bracket_pos(self):
self._study2()
return self.lastopenbracketpos

View File

@ -1,794 +0,0 @@
#! /usr/bin/env python
import os
import sys
import string
import getopt
import re
import warnings
import types
import linecache
from code import InteractiveInterpreter
from Tkinter import *
import tkMessageBox
from EditorWindow import EditorWindow, fixwordbreaks
from FileList import FileList
from ColorDelegator import ColorDelegator
from UndoDelegator import UndoDelegator
from OutputWindow import OutputWindow
from IdleConf import idleconf
import idlever
# We need to patch linecache.checkcache, because we don't want it
# to throw away our <pyshell#...> entries.
# Rather than repeating its code here, we save those entries,
# then call the original function, and then restore the saved entries.
def linecache_checkcache(orig_checkcache=linecache.checkcache):
cache = linecache.cache
save = {}
for filename in cache.keys():
if filename[:1] + filename[-1:] == '<>':
save[filename] = cache[filename]
orig_checkcache()
cache.update(save)
linecache.checkcache = linecache_checkcache
IDENTCHARS = string.ascii_letters + string.digits + "_"
# Note: <<newline-and-indent>> event is defined in AutoIndent.py
#$ event <<plain-newline-and-indent>>
#$ win <Control-j>
#$ unix <Control-j>
#$ event <<beginning-of-line>>
#$ win <Control-a>
#$ win <Home>
#$ unix <Control-a>
#$ unix <Home>
#$ event <<history-next>>
#$ win <Alt-n>
#$ unix <Alt-n>
#$ event <<history-previous>>
#$ win <Alt-p>
#$ unix <Alt-p>
#$ event <<interrupt-execution>>
#$ win <Control-c>
#$ unix <Control-c>
#$ event <<end-of-file>>
#$ win <Control-d>
#$ unix <Control-d>
#$ event <<open-stack-viewer>>
#$ event <<toggle-debugger>>
class PyShellEditorWindow(EditorWindow):
# Regular text edit window when a shell is present
# XXX ought to merge with regular editor window
runnable = True # Shell not present, enable Import Module and Run Script
def __init__(self, *args):
apply(EditorWindow.__init__, (self,) + args)
self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)
self.text.bind("<<open-python-shell>>", self.flist.open_shell)
rmenu_specs = [
("Set breakpoint here", "<<set-breakpoint-here>>"),
]
def set_breakpoint_here(self, event=None):
if not self.flist.pyshell or not self.flist.pyshell.interp.debugger:
self.text.bell()
return
self.flist.pyshell.interp.debugger.set_breakpoint_here(self)
class PyShellFileList(FileList):
# File list when a shell is present
EditorWindow = PyShellEditorWindow
pyshell = None
def open_shell(self, event=None):
if self.pyshell:
self.pyshell.wakeup()
else:
self.pyshell = PyShell(self)
self.pyshell.begin()
return self.pyshell
class ModifiedColorDelegator(ColorDelegator):
# Colorizer for the shell window itself
def recolorize_main(self):
self.tag_remove("TODO", "1.0", "iomark")
self.tag_add("SYNC", "1.0", "iomark")
ColorDelegator.recolorize_main(self)
tagdefs = ColorDelegator.tagdefs.copy()
cconf = idleconf.getsection('Colors')
tagdefs.update({
"stdin": cconf.getcolor("stdin"),
"stdout": cconf.getcolor("stdout"),
"stderr": cconf.getcolor("stderr"),
"console": cconf.getcolor("console"),
"ERROR": cconf.getcolor("ERROR"),
None: cconf.getcolor("normal"),
})
class ModifiedUndoDelegator(UndoDelegator):
# Forbid insert/delete before the I/O mark
def insert(self, index, chars, tags=None):
try:
if self.delegate.compare(index, "<", "iomark"):
self.delegate.bell()
return
except TclError:
pass
UndoDelegator.insert(self, index, chars, tags)
def delete(self, index1, index2=None):
try:
if self.delegate.compare(index1, "<", "iomark"):
self.delegate.bell()
return
except TclError:
pass
UndoDelegator.delete(self, index1, index2)
class ModifiedInterpreter(InteractiveInterpreter):
def __init__(self, tkconsole):
self.tkconsole = tkconsole
locals = sys.modules['__main__'].__dict__
InteractiveInterpreter.__init__(self, locals=locals)
self.save_warnings_filters = None
gid = 0
def execsource(self, source):
# Like runsource() but assumes complete exec source
filename = self.stuffsource(source)
self.execfile(filename, source)
def execfile(self, filename, source=None):
# Execute an existing file
if source is None:
source = open(filename, "r").read()
try:
code = compile(source, filename, "exec")
except (OverflowError, SyntaxError):
self.tkconsole.resetoutput()
InteractiveInterpreter.showsyntaxerror(self, filename)
else:
self.runcode(code)
def runsource(self, source):
# Extend base class to stuff the source in the line cache first
filename = self.stuffsource(source)
self.more = 0
self.save_warnings_filters = warnings.filters[:]
warnings.filterwarnings(action="error", category=SyntaxWarning)
if isinstance(source, types.UnicodeType):
import IOBinding
try:
source = source.encode(IOBinding.encoding)
except UnicodeError:
self.tkconsole.resetoutput()
self.write("Unsupported characters in input")
return
try:
return InteractiveInterpreter.runsource(self, source, filename)
finally:
if self.save_warnings_filters is not None:
warnings.filters[:] = self.save_warnings_filters
self.save_warnings_filters = None
def stuffsource(self, source):
# Stuff source in the filename cache
filename = "<pyshell#%d>" % self.gid
self.gid = self.gid + 1
lines = source.split("\n")
linecache.cache[filename] = len(source)+1, 0, lines, filename
return filename
def showsyntaxerror(self, filename=None):
# Extend base class to color the offending position
# (instead of printing it and pointing at it with a caret)
text = self.tkconsole.text
stuff = self.unpackerror()
if not stuff:
self.tkconsole.resetoutput()
InteractiveInterpreter.showsyntaxerror(self, filename)
return
msg, lineno, offset, line = stuff
if lineno == 1:
pos = "iomark + %d chars" % (offset-1)
else:
pos = "iomark linestart + %d lines + %d chars" % (lineno-1,
offset-1)
text.tag_add("ERROR", pos)
text.see(pos)
char = text.get(pos)
if char and char in IDENTCHARS:
text.tag_add("ERROR", pos + " wordstart", pos)
self.tkconsole.resetoutput()
self.write("SyntaxError: %s\n" % str(msg))
def unpackerror(self):
type, value, tb = sys.exc_info()
ok = type is SyntaxError
if ok:
try:
msg, (dummy_filename, lineno, offset, line) = value
except:
ok = 0
if ok:
return msg, lineno, offset, line
else:
return None
def showtraceback(self):
# Extend base class method to reset output properly
self.tkconsole.resetoutput()
self.checklinecache()
InteractiveInterpreter.showtraceback(self)
def checklinecache(self):
c = linecache.cache
for key in c.keys():
if key[:1] + key[-1:] != "<>":
del c[key]
debugger = None
def setdebugger(self, debugger):
self.debugger = debugger
def getdebugger(self):
return self.debugger
def runcode(self, code):
# Override base class method
if self.save_warnings_filters is not None:
warnings.filters[:] = self.save_warnings_filters
self.save_warnings_filters = None
debugger = self.debugger
try:
self.tkconsole.beginexecuting()
try:
if debugger:
debugger.run(code, self.locals)
else:
exec code in self.locals
except SystemExit:
if tkMessageBox.askyesno(
"Exit?",
"Do you want to exit altogether?",
default="yes",
master=self.tkconsole.text):
raise
else:
self.showtraceback()
if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
self.tkconsole.open_stack_viewer()
except:
self.showtraceback()
if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
self.tkconsole.open_stack_viewer()
finally:
self.tkconsole.endexecuting()
def write(self, s):
# Override base class write
self.tkconsole.console.write(s)
class PyShell(OutputWindow):
shell_title = "Python Shell"
# Override classes
ColorDelegator = ModifiedColorDelegator
UndoDelegator = ModifiedUndoDelegator
# Override menu bar specs
menu_specs = PyShellEditorWindow.menu_specs[:]
menu_specs.insert(len(menu_specs)-2, ("debug", "_Debug"))
# New classes
from IdleHistory import History
def __init__(self, flist=None):
self.interp = ModifiedInterpreter(self)
if flist is None:
root = Tk()
fixwordbreaks(root)
root.withdraw()
flist = PyShellFileList(root)
OutputWindow.__init__(self, flist, None, None)
import __builtin__
__builtin__.quit = __builtin__.exit = "To exit, type Ctrl-D."
self.auto = self.extensions["AutoIndent"] # Required extension
self.auto.config(usetabs=1, indentwidth=8, context_use_ps1=1)
text = self.text
text.configure(wrap="char")
text.bind("<<newline-and-indent>>", self.enter_callback)
text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
text.bind("<<interrupt-execution>>", self.cancel_callback)
text.bind("<<beginning-of-line>>", self.home_callback)
text.bind("<<end-of-file>>", self.eof_callback)
text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
text.bind("<<toggle-debugger>>", self.toggle_debugger)
text.bind("<<open-python-shell>>", self.flist.open_shell)
text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
self.save_stdout = sys.stdout
self.save_stderr = sys.stderr
self.save_stdin = sys.stdin
sys.stdout = PseudoFile(self, "stdout")
sys.stderr = PseudoFile(self, "stderr")
sys.stdin = self
self.console = PseudoFile(self, "console")
self.history = self.History(self.text)
reading = 0
executing = 0
canceled = 0
endoffile = 0
def toggle_debugger(self, event=None):
if self.executing:
tkMessageBox.showerror("Don't debug now",
"You can only toggle the debugger when idle",
master=self.text)
self.set_debugger_indicator()
return "break"
else:
db = self.interp.getdebugger()
if db:
self.close_debugger()
else:
self.open_debugger()
def set_debugger_indicator(self):
db = self.interp.getdebugger()
self.setvar("<<toggle-debugger>>", not not db)
def toggle_jit_stack_viewer( self, event=None):
pass # All we need is the variable
def close_debugger(self):
db = self.interp.getdebugger()
if db:
self.interp.setdebugger(None)
db.close()
self.resetoutput()
self.console.write("[DEBUG OFF]\n")
sys.ps1 = ">>> "
self.showprompt()
self.set_debugger_indicator()
def open_debugger(self):
import Debugger
self.interp.setdebugger(Debugger.Debugger(self))
sys.ps1 = "[DEBUG ON]\n>>> "
self.showprompt()
self.set_debugger_indicator()
def beginexecuting(self):
# Helper for ModifiedInterpreter
self.resetoutput()
self.executing = 1
##self._cancel_check = self.cancel_check
##sys.settrace(self._cancel_check)
def endexecuting(self):
# Helper for ModifiedInterpreter
##sys.settrace(None)
##self._cancel_check = None
self.executing = 0
self.canceled = 0
def close(self):
# Extend base class method
if self.executing:
# XXX Need to ask a question here
if not tkMessageBox.askokcancel(
"Kill?",
"The program is still running; do you want to kill it?",
default="ok",
master=self.text):
return "cancel"
self.canceled = 1
if self.reading:
self.top.quit()
return "cancel"
return OutputWindow.close(self)
def _close(self):
self.close_debugger()
# Restore std streams
sys.stdout = self.save_stdout
sys.stderr = self.save_stderr
sys.stdin = self.save_stdin
# Break cycles
self.interp = None
self.console = None
self.auto = None
self.flist.pyshell = None
self.history = None
OutputWindow._close(self) # Really EditorWindow._close
def ispythonsource(self, filename):
# Override this so EditorWindow never removes the colorizer
return True
def short_title(self):
return self.shell_title
COPYRIGHT = \
'Type "copyright", "credits" or "license" for more information.'
def begin(self):
self.resetoutput()
self.write("Python %s on %s\n%s\nIDLE %s -- press F1 for help\n" %
(sys.version, sys.platform, self.COPYRIGHT,
idlever.IDLE_VERSION))
try:
sys.ps1
except AttributeError:
sys.ps1 = ">>> "
self.showprompt()
import Tkinter
Tkinter._default_root = None
def interact(self):
self.begin()
self.top.mainloop()
def readline(self):
save = self.reading
try:
self.reading = 1
self.top.mainloop()
finally:
self.reading = save
line = self.text.get("iomark", "end-1c")
self.resetoutput()
if self.canceled:
self.canceled = 0
raise KeyboardInterrupt
if self.endoffile:
self.endoffile = 0
return ""
return line
def isatty(self):
return True
def cancel_callback(self, event):
try:
if self.text.compare("sel.first", "!=", "sel.last"):
return # Active selection -- always use default binding
except:
pass
if not (self.executing or self.reading):
self.resetoutput()
self.write("KeyboardInterrupt\n")
self.showprompt()
return "break"
self.endoffile = 0
self.canceled = 1
if self.reading:
self.top.quit()
return "break"
def eof_callback(self, event):
if self.executing and not self.reading:
return # Let the default binding (delete next char) take over
if not (self.text.compare("iomark", "==", "insert") and
self.text.compare("insert", "==", "end-1c")):
return # Let the default binding (delete next char) take over
if not self.executing:
## if not tkMessageBox.askokcancel(
## "Exit?",
## "Are you sure you want to exit?",
## default="ok", master=self.text):
## return "break"
self.resetoutput()
self.close()
else:
self.canceled = 0
self.endoffile = 1
self.top.quit()
return "break"
def home_callback(self, event):
if event.state != 0 and event.keysym == "Home":
return # <Modifier-Home>; fall back to class binding
if self.text.compare("iomark", "<=", "insert") and \
self.text.compare("insert linestart", "<=", "iomark"):
self.text.mark_set("insert", "iomark")
self.text.tag_remove("sel", "1.0", "end")
self.text.see("insert")
return "break"
def linefeed_callback(self, event):
# Insert a linefeed without entering anything (still autoindented)
if self.reading:
self.text.insert("insert", "\n")
self.text.see("insert")
else:
self.auto.auto_indent(event)
return "break"
def enter_callback(self, event):
if self.executing and not self.reading:
return # Let the default binding (insert '\n') take over
# If some text is selected, recall the selection
# (but only if this before the I/O mark)
try:
sel = self.text.get("sel.first", "sel.last")
if sel:
if self.text.compare("sel.last", "<=", "iomark"):
self.recall(sel)
return "break"
except:
pass
# If we're strictly before the line containing iomark, recall
# the current line, less a leading prompt, less leading or
# trailing whitespace
if self.text.compare("insert", "<", "iomark linestart"):
# Check if there's a relevant stdin range -- if so, use it
prev = self.text.tag_prevrange("stdin", "insert")
if prev and self.text.compare("insert", "<", prev[1]):
self.recall(self.text.get(prev[0], prev[1]))
return "break"
next = self.text.tag_nextrange("stdin", "insert")
if next and self.text.compare("insert lineend", ">=", next[0]):
self.recall(self.text.get(next[0], next[1]))
return "break"
# No stdin mark -- just get the current line
self.recall(self.text.get("insert linestart", "insert lineend"))
return "break"
# If we're in the current input and there's only whitespace
# beyond the cursor, erase that whitespace first
s = self.text.get("insert", "end-1c")
if s and not s.strip():
self.text.delete("insert", "end-1c")
# If we're in the current input before its last line,
# insert a newline right at the insert point
if self.text.compare("insert", "<", "end-1c linestart"):
self.auto.auto_indent(event)
return "break"
# We're in the last line; append a newline and submit it
self.text.mark_set("insert", "end-1c")
if self.reading:
self.text.insert("insert", "\n")
self.text.see("insert")
else:
self.auto.auto_indent(event)
self.text.tag_add("stdin", "iomark", "end-1c")
self.text.update_idletasks()
if self.reading:
self.top.quit() # Break out of recursive mainloop() in raw_input()
else:
self.runit()
return "break"
def recall(self, s):
if self.history:
self.history.recall(s)
def runit(self):
line = self.text.get("iomark", "end-1c")
# Strip off last newline and surrounding whitespace.
# (To allow you to hit return twice to end a statement.)
i = len(line)
while i > 0 and line[i-1] in " \t":
i = i-1
if i > 0 and line[i-1] == "\n":
i = i-1
while i > 0 and line[i-1] in " \t":
i = i-1
line = line[:i]
more = self.interp.runsource(line)
if not more:
self.showprompt()
def cancel_check(self, frame, what, args,
dooneevent=tkinter.dooneevent,
dontwait=tkinter.DONT_WAIT):
# Hack -- use the debugger hooks to be able to handle events
# and interrupt execution at any time.
# This slows execution down quite a bit, so you may want to
# disable this (by not calling settrace() in runcode() above)
# for full-bore (uninterruptable) speed.
# XXX This should become a user option.
if self.canceled:
return
dooneevent(dontwait)
if self.canceled:
self.canceled = 0
raise KeyboardInterrupt
return self._cancel_check
def open_stack_viewer(self, event=None):
try:
sys.last_traceback
except:
tkMessageBox.showerror("No stack trace",
"There is no stack trace yet.\n"
"(sys.last_traceback is not defined)",
master=self.text)
return
from StackViewer import StackBrowser
sv = StackBrowser(self.root, self.flist)
def showprompt(self):
self.resetoutput()
try:
s = str(sys.ps1)
except:
s = ""
self.console.write(s)
self.text.mark_set("insert", "end-1c")
def resetoutput(self):
source = self.text.get("iomark", "end-1c")
if self.history:
self.history.history_store(source)
if self.text.get("end-2c") != "\n":
self.text.insert("end-1c", "\n")
self.text.mark_set("iomark", "end-1c")
sys.stdout.softspace = 0
def write(self, s, tags=()):
self.text.mark_gravity("iomark", "right")
OutputWindow.write(self, s, tags, "iomark")
self.text.mark_gravity("iomark", "left")
if self.canceled:
self.canceled = 0
raise KeyboardInterrupt
class PseudoFile:
def __init__(self, shell, tags):
self.shell = shell
self.tags = tags
def write(self, s):
self.shell.write(s, self.tags)
def writelines(self, l):
map(self.write, l)
def flush(self):
pass
def isatty(self):
return True
usage_msg = """\
usage: idle.py [-c command] [-d] [-e] [-s] [-t title] [arg] ...
-c command run this command
-d enable debugger
-e edit mode; arguments are files to be edited
-s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
-t title set title of shell window
When neither -c nor -e is used, and there are arguments, and the first
argument is not '-', the first argument is run as a script. Remaining
arguments are arguments to the script or to the command run by -c.
"""
def main():
cmd = None
edit = 0
debug = 0
startup = 0
try:
opts, args = getopt.getopt(sys.argv[1:], "c:deist:")
except getopt.error, msg:
sys.stderr.write("Error: %s\n" % str(msg))
sys.stderr.write(usage_msg)
sys.exit(2)
for o, a in opts:
if o == '-c':
cmd = a
if o == '-d':
debug = 1
if o == '-e':
edit = 1
if o == '-s':
startup = 1
if o == '-t':
PyShell.shell_title = a
for i in range(len(sys.path)):
sys.path[i] = os.path.abspath(sys.path[i])
pathx = []
if edit:
for filename in args:
pathx.append(os.path.dirname(filename))
elif args and args[0] != "-":
pathx.append(os.path.dirname(args[0]))
else:
pathx.append(os.curdir)
for dir in pathx:
dir = os.path.abspath(dir)
if not dir in sys.path:
sys.path.insert(0, dir)
global flist, root
root = Tk(className="Idle")
fixwordbreaks(root)
root.withdraw()
flist = PyShellFileList(root)
if edit:
for filename in args:
flist.open(filename)
else:
if cmd:
sys.argv = ["-c"] + args
else:
sys.argv = args or [""]
shell = PyShell(flist)
interp = shell.interp
flist.pyshell = shell
if startup:
filename = os.environ.get("IDLESTARTUP") or \
os.environ.get("PYTHONSTARTUP")
if filename and os.path.isfile(filename):
interp.execfile(filename)
if debug:
shell.open_debugger()
if cmd:
interp.execsource(cmd)
elif not edit and args and args[0] != "-":
interp.execfile(args[0])
shell.begin()
root.mainloop()
root.destroy()
if __name__ == "__main__":
main()

View File

@ -1,53 +0,0 @@
IDLE 0.5 - February 2000
------------------------
This is an early release of IDLE, my own attempt at a Tkinter-based
IDE for Python.
For news about this release, see the file NEWS.txt. (For a more
detailed change log, see the file ChangeLog.)
FEATURES
IDLE has the following features:
- coded in 100% pure Python, using the Tkinter GUI toolkit (i.e. Tcl/Tk)
- cross-platform: works on Windows and Unix (on the Mac, there are
currently problems with Tcl/Tk)
- multi-window text editor with multiple undo, Python colorizing
and many other features, e.g. smart indent and call tips
- Python shell window (a.k.a. interactive interpreter)
- debugger (not complete, but you can set breakpoints, view and step)
USAGE
The main program is in the file "idle.py"; on Unix, you should be able
to run it by typing "./idle.py" to your shell. On Windows, you can
run it by double-clicking it; you can use idle.pyw to avoid popping up
a DOS console. If you want to pass command line arguments on Windows,
use the batch file idle.bat.
Command line arguments: files passed on the command line are executed,
not opened for editing, unless you give the -e command line option.
Try "./idle.py -h" to see other command line options.
IDLE requires Python 1.5.2 or later, so it is currently only usable
with a Python 1.5.2 (or later) distribution. (An older version of
IDLE is distributed with Python 1.5.2; you can drop this version on
top of it.)
COPYRIGHT
IDLE is covered by the standard Python copyright notice
(http://www.python.org/doc/Copyright.html).
FEEDBACK
For feedback, please use the Python Bugs List
(http://www.python.org/search/search_bugs.html).
--Guido van Rossum (home page: http://www.python.org/~guido/)

View File

@ -1,341 +0,0 @@
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)

View File

@ -1,167 +0,0 @@
from Tkinter import *
import SearchEngine
from SearchDialogBase import SearchDialogBase
def replace(text):
root = text._root()
engine = SearchEngine.get(root)
if not hasattr(engine, "_replacedialog"):
engine._replacedialog = ReplaceDialog(root, engine)
dialog = engine._replacedialog
dialog.open(text)
class ReplaceDialog(SearchDialogBase):
title = "Replace Dialog"
icon = "Replace"
def __init__(self, root, engine):
SearchDialogBase.__init__(self, root, engine)
self.replvar = StringVar(root)
def open(self, text):
SearchDialogBase.open(self, text)
try:
first = text.index("sel.first")
except TclError:
first = None
try:
last = text.index("sel.last")
except TclError:
last = None
first = first or text.index("insert")
last = last or first
self.show_hit(first, last)
self.ok = 1
def create_entries(self):
SearchDialogBase.create_entries(self)
self.replent = self.make_entry("Replace with:", self.replvar)
def create_command_buttons(self):
SearchDialogBase.create_command_buttons(self)
self.make_button("Find", self.find_it)
self.make_button("Replace", self.replace_it)
self.make_button("Replace+Find", self.default_command, 1)
self.make_button("Replace All", self.replace_all)
def find_it(self, event=None):
self.do_find(0)
def replace_it(self, event=None):
if self.do_find(self.ok):
self.do_replace()
def default_command(self, event=None):
if self.do_find(self.ok):
self.do_replace()
self.do_find(0)
def replace_all(self, event=None):
prog = self.engine.getprog()
if not prog:
return
repl = self.replvar.get()
text = self.text
res = self.engine.search_text(text, prog)
if not res:
text.bell()
return
text.tag_remove("sel", "1.0", "end")
text.tag_remove("hit", "1.0", "end")
line = res[0]
col = res[1].start()
if self.engine.iswrap():
line = 1
col = 0
ok = 1
first = last = None
# XXX ought to replace circular instead of top-to-bottom when wrapping
text.undo_block_start()
while 1:
res = self.engine.search_forward(text, prog, line, col, 0, ok)
if not res:
break
line, m = res
chars = text.get("%d.0" % line, "%d.0" % (line+1))
orig = m.group()
new = m.expand(repl)
i, j = m.span()
first = "%d.%d" % (line, i)
last = "%d.%d" % (line, j)
if new == orig:
text.mark_set("insert", last)
else:
text.mark_set("insert", first)
if first != last:
text.delete(first, last)
if new:
text.insert(first, new)
col = i + len(new)
ok = 0
text.undo_block_stop()
if first and last:
self.show_hit(first, last)
self.close()
def do_find(self, ok=0):
if not self.engine.getprog():
return False
text = self.text
res = self.engine.search_text(text, None, ok)
if not res:
text.bell()
return False
line, m = res
i, j = m.span()
first = "%d.%d" % (line, i)
last = "%d.%d" % (line, j)
self.show_hit(first, last)
self.ok = 1
return True
def do_replace(self):
prog = self.engine.getprog()
if not prog:
return False
text = self.text
try:
first = pos = text.index("sel.first")
last = text.index("sel.last")
except TclError:
pos = None
if not pos:
first = last = pos = text.index("insert")
line, col = SearchEngine.get_line_col(pos)
chars = text.get("%d.0" % line, "%d.0" % (line+1))
m = prog.match(chars, col)
if not prog:
return False
new = m.expand(self.replvar.get())
text.mark_set("insert", first)
text.undo_block_start()
if m.group():
text.delete(first, last)
if new:
text.insert(first, new)
text.undo_block_stop()
self.show_hit(first, text.index("insert"))
self.ok = 0
return True
def show_hit(self, first, last):
text = self.text
text.mark_set("insert", first)
text.tag_remove("sel", "1.0", "end")
text.tag_add("sel", first, last)
text.tag_remove("hit", "1.0", "end")
if first == last:
text.tag_add("hit", first)
else:
text.tag_add("hit", first, last)
text.see("insert")
text.update_idletasks()
def close(self, event=None):
SearchDialogBase.close(self, event)
self.text.tag_remove("hit", "1.0", "end")

View File

@ -1,175 +0,0 @@
"""Extension to execute code outside the Python shell window.
This adds the following commands (to the Edit menu, until there's a
separate Python menu):
- Check module (Alt-F5) does a full syntax check of the current module.
It also runs the tabnanny to catch any inconsistent tabs.
- Import module (F5) is equivalent to either import or reload of the
current module. The window must have been saved previously. The
module is added to sys.modules, and is also added to the __main__
namespace. Output goes to the shell window.
- Run module (Control-F5) does the same but executes the module's
code in the __main__ namespace.
"""
import sys
import os
import imp
import tkMessageBox
indent_message = """Error: Inconsistent indentation detected!
This means that either:
(1) your indentation is outright incorrect (easy to fix), or
(2) your indentation mixes tabs and spaces in a way that depends on \
how many spaces a tab is worth.
To fix case 2, change all tabs to spaces by using Select All followed \
by Untabify Region (both in the Edit menu)."""
class ScriptBinding:
keydefs = {
'<<check-module>>': ['<Alt-F5>', '<Meta-F5>'],
'<<import-module>>': ['<F5>'],
'<<run-script>>': ['<Control-F5>'],
}
menudefs = [
('edit', [None,
('Check module', '<<check-module>>'),
('Import module', '<<import-module>>'),
('Run script', '<<run-script>>'),
]
),
]
def __init__(self, editwin):
if not editwin.runnable:
self.menudefs = []
self.keydefs = {}
self.editwin = editwin
# Provide instance variables referenced by Debugger
# XXX This should be done differently
self.flist = self.editwin.flist
self.root = self.flist.root
def check_module_event(self, event):
filename = self.getfilename()
if not filename:
return
if not self.tabnanny(filename):
return
if not self.checksyntax(filename):
return
def tabnanny(self, filename):
import tabnanny
import tokenize
f = open(filename, 'r')
try:
tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
except tokenize.TokenError, msg:
self.errorbox("Token error",
"Token error:\n%s" % str(msg))
return 0
except tabnanny.NannyNag, nag:
# The error messages from tabnanny are too confusing...
self.editwin.gotoline(nag.get_lineno())
self.errorbox("Tab/space error", indent_message)
return 0
return 1
def checksyntax(self, filename):
f = open(filename, 'r')
source = f.read()
f.close()
if '\r' in source:
import re
source = re.sub(r"\r\n", "\n", source)
if source and source[-1] != '\n':
source = source + '\n'
try:
compile(source, filename, "exec")
except (SyntaxError, OverflowError), err:
try:
msg, (errorfilename, lineno, offset, line) = err
if not errorfilename:
err.args = msg, (filename, lineno, offset, line)
err.filename = filename
except:
lineno = None
msg = "*** " + str(err)
if lineno:
self.editwin.gotoline(lineno)
self.errorbox("Syntax error",
"There's an error in your program:\n" + msg)
return 1
def import_module_event(self, event):
filename = self.getfilename()
if not filename:
return
modname, ext = os.path.splitext(os.path.basename(filename))
if sys.modules.has_key(modname):
mod = sys.modules[modname]
else:
mod = imp.new_module(modname)
sys.modules[modname] = mod
mod.__file__ = filename
setattr(sys.modules['__main__'], modname, mod)
dir = os.path.dirname(filename)
dir = os.path.normpath(os.path.abspath(dir))
if dir not in sys.path:
sys.path.insert(0, dir)
flist = self.editwin.flist
shell = flist.open_shell()
interp = shell.interp
interp.runcode("reload(%s)" % modname)
def run_script_event(self, event):
filename = self.getfilename()
if not filename:
return
flist = self.editwin.flist
shell = flist.open_shell()
interp = shell.interp
if (not sys.argv or
os.path.basename(sys.argv[0]) != os.path.basename(filename)):
# XXX Too often this discards arguments the user just set...
sys.argv = [filename]
interp.execfile(filename)
def getfilename(self):
# Logic to make sure we have a saved filename
# XXX Better logic would offer to save!
if not self.editwin.get_saved():
name = (self.editwin.short_title() or
self.editwin.long_title() or
"Untitled")
self.errorbox("Not saved",
"The buffer for %s is not saved.\n" % name +
"Please save it first!")
self.editwin.text.focus_set()
return
filename = self.editwin.io.filename
if not filename:
self.errorbox("No file name",
"This window has no file name")
return
return filename
def errorbox(self, title, message):
# XXX This should really be a function of EditorWindow...
tkMessageBox.showerror(title, message, master=self.editwin.text)
self.editwin.text.focus_set()

View File

@ -1,139 +0,0 @@
from Tkinter import *
class ScrolledList:
default = "(None)"
def __init__(self, master, **options):
# Create top frame, with scrollbar and listbox
self.master = master
self.frame = frame = Frame(master)
self.frame.pack(fill="both", expand=1)
self.vbar = vbar = Scrollbar(frame, name="vbar")
self.vbar.pack(side="right", fill="y")
self.listbox = listbox = Listbox(frame, exportselection=0,
background="white")
if options:
listbox.configure(options)
listbox.pack(expand=1, fill="both")
# Tie listbox and scrollbar together
vbar["command"] = listbox.yview
listbox["yscrollcommand"] = vbar.set
# Bind events to the list box
listbox.bind("<ButtonRelease-1>", self.click_event)
listbox.bind("<Double-ButtonRelease-1>", self.double_click_event)
listbox.bind("<ButtonPress-3>", self.popup_event)
listbox.bind("<Key-Up>", self.up_event)
listbox.bind("<Key-Down>", self.down_event)
# Mark as empty
self.clear()
def close(self):
self.frame.destroy()
def clear(self):
self.listbox.delete(0, "end")
self.empty = 1
self.listbox.insert("end", self.default)
def append(self, item):
if self.empty:
self.listbox.delete(0, "end")
self.empty = 0
self.listbox.insert("end", str(item))
def get(self, index):
return self.listbox.get(index)
def click_event(self, event):
self.listbox.activate("@%d,%d" % (event.x, event.y))
index = self.listbox.index("active")
self.select(index)
self.on_select(index)
return "break"
def double_click_event(self, event):
index = self.listbox.index("active")
self.select(index)
self.on_double(index)
return "break"
menu = None
def popup_event(self, event):
if not self.menu:
self.make_menu()
menu = self.menu
self.listbox.activate("@%d,%d" % (event.x, event.y))
index = self.listbox.index("active")
self.select(index)
menu.tk_popup(event.x_root, event.y_root)
def make_menu(self):
menu = Menu(self.listbox, tearoff=0)
self.menu = menu
self.fill_menu()
def up_event(self, event):
index = self.listbox.index("active")
if self.listbox.selection_includes(index):
index = index - 1
else:
index = self.listbox.size() - 1
if index < 0:
self.listbox.bell()
else:
self.select(index)
self.on_select(index)
return "break"
def down_event(self, event):
index = self.listbox.index("active")
if self.listbox.selection_includes(index):
index = index + 1
else:
index = 0
if index >= self.listbox.size():
self.listbox.bell()
else:
self.select(index)
self.on_select(index)
return "break"
def select(self, index):
self.listbox.focus_set()
self.listbox.activate(index)
self.listbox.selection_clear(0, "end")
self.listbox.selection_set(index)
self.listbox.see(index)
# Methods to override for specific actions
def fill_menu(self):
pass
def on_select(self, index):
pass
def on_double(self, index):
pass
def test():
root = Tk()
root.protocol("WM_DELETE_WINDOW", root.destroy)
class MyScrolledList(ScrolledList):
def fill_menu(self): self.menu.add_command(label="pass")
def on_select(self, index): print "select", self.get(index)
def on_double(self, index): print "double", self.get(index)
s = MyScrolledList(root)
for i in range(30):
s.append("item %02d" % i)
return root
def main():
root = test()
root.mainloop()
if __name__ == '__main__':
main()

View File

@ -1,97 +0,0 @@
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")

View File

@ -1,67 +0,0 @@
from Tkinter import *
import SearchEngine
from SearchDialogBase import SearchDialogBase
def _setup(text):
root = text._root()
engine = SearchEngine.get(root)
if not hasattr(engine, "_searchdialog"):
engine._searchdialog = SearchDialog(root, engine)
return engine._searchdialog
def find(text):
return _setup(text).open(text)
def find_again(text):
return _setup(text).find_again(text)
def find_selection(text):
return _setup(text).find_selection(text)
class SearchDialog(SearchDialogBase):
def create_widgets(self):
f = SearchDialogBase.create_widgets(self)
self.make_button("Find", self.default_command, 1)
def default_command(self, event=None):
if not self.engine.getprog():
return
if self.find_again(self.text):
self.close()
def find_again(self, text):
if not self.engine.getpat():
self.open(text)
return False
if not self.engine.getprog():
return False
res = self.engine.search_text(text)
if res:
line, m = res
i, j = m.span()
first = "%d.%d" % (line, i)
last = "%d.%d" % (line, j)
try:
selfirst = text.index("sel.first")
sellast = text.index("sel.last")
if selfirst == first and sellast == last:
text.bell()
return False
except TclError:
pass
text.tag_remove("sel", "1.0", "end")
text.tag_add("sel", first, last)
text.mark_set("insert", self.engine.isback() and first or last)
text.see("insert")
return True
else:
text.bell()
return False
def find_selection(self, text):
pat = text.get("sel.first", "sel.last")
if pat:
self.engine.setcookedpat(pat)
return self.find_again(text)

View File

@ -1,128 +0,0 @@
from Tkinter import *
class SearchDialogBase:
title = "Search Dialog"
icon = "Search"
needwrapbutton = 1
def __init__(self, root, engine):
self.root = root
self.engine = engine
self.top = None
def open(self, text):
self.text = text
if not self.top:
self.create_widgets()
else:
self.top.deiconify()
self.top.tkraise()
self.ent.focus_set()
self.ent.selection_range(0, "end")
self.ent.icursor(0)
self.top.grab_set()
def close(self, event=None):
if self.top:
self.top.grab_release()
self.top.withdraw()
def create_widgets(self):
top = Toplevel(self.root)
top.bind("<Return>", self.default_command)
top.bind("<Escape>", self.close)
top.protocol("WM_DELETE_WINDOW", self.close)
top.wm_title(self.title)
top.wm_iconname(self.icon)
self.top = top
self.row = 0
self.top.grid_columnconfigure(0, weight=0)
self.top.grid_columnconfigure(1, weight=100)
self.create_entries()
self.create_option_buttons()
self.create_other_buttons()
return self.create_command_buttons()
def make_entry(self, label, var):
l = Label(self.top, text=label)
l.grid(row=self.row, column=0, sticky="w")
e = Entry(self.top, textvariable=var, exportselection=0)
e.grid(row=self.row, column=1, sticky="we")
self.row = self.row + 1
return e
def make_frame(self):
f = Frame(self.top)
f.grid(row=self.row, column=0, columnspan=2, sticky="we")
self.row = self.row + 1
return f
def make_button(self, label, command, isdef=0, side="left"):
b = Button(self.buttonframe,
text=label, command=command,
default=isdef and "active" or "normal")
b.pack(side=side)
return b
def create_entries(self):
self.ent = self.make_entry("Find:", self.engine.patvar)
def create_option_buttons(self):
f = self.make_frame()
btn = Checkbutton(f, anchor="w",
variable=self.engine.revar,
text="Regular expression")
btn.pack(side="left", fill="both")
if self.engine.isre():
btn.select()
btn = Checkbutton(f, anchor="w",
variable=self.engine.casevar,
text="Match case")
btn.pack(side="left", fill="both")
if self.engine.iscase():
btn.select()
btn = Checkbutton(f, anchor="w",
variable=self.engine.wordvar,
text="Whole word")
btn.pack(side="left", fill="both")
if self.engine.isword():
btn.select()
if self.needwrapbutton:
btn = Checkbutton(f, anchor="w",
variable=self.engine.wrapvar,
text="Wrap around")
btn.pack(side="left", fill="both")
if self.engine.iswrap():
btn.select()
def create_other_buttons(self):
f = self.make_frame()
lbl = Label(f, text="Direction: ")
lbl.pack(side="left")
btn = Radiobutton(f, anchor="w",
variable=self.engine.backvar, value=1,
text="Up")
btn.pack(side="left", fill="both")
if self.engine.isback():
btn.select()
btn = Radiobutton(f, anchor="w",
variable=self.engine.backvar, value=0,
text="Down")
btn.pack(side="left", fill="both")
if not self.engine.isback():
btn.select()
def create_command_buttons(self):
f = self.buttonframe = self.make_frame()
b = self.make_button("close", self.close, side="right")
b.lower()

View File

@ -1,220 +0,0 @@
import re
from Tkinter import *
import tkMessageBox
def get(root):
if not hasattr(root, "_searchengine"):
root._searchengine = SearchEngine(root)
# XXX This will never garbage-collect -- who cares
return root._searchengine
class SearchEngine:
def __init__(self, root):
self.root = root
# State shared by search, replace, and grep;
# the search dialogs bind these to UI elements.
self.patvar = StringVar(root) # search pattern
self.revar = BooleanVar(root) # regular expression?
self.casevar = BooleanVar(root) # match case?
self.wordvar = BooleanVar(root) # match whole word?
self.wrapvar = BooleanVar(root) # wrap around buffer?
self.wrapvar.set(1) # (on by default)
self.backvar = BooleanVar(root) # search backwards?
# Access methods
def getpat(self):
return self.patvar.get()
def setpat(self, pat):
self.patvar.set(pat)
def isre(self):
return self.revar.get()
def iscase(self):
return self.casevar.get()
def isword(self):
return self.wordvar.get()
def iswrap(self):
return self.wrapvar.get()
def isback(self):
return self.backvar.get()
# Higher level access methods
def getcookedpat(self):
pat = self.getpat()
if not self.isre():
pat = re.escape(pat)
if self.isword():
pat = r"\b%s\b" % pat
return pat
def getprog(self):
pat = self.getpat()
if not pat:
self.report_error(pat, "Empty regular expression")
return None
pat = self.getcookedpat()
flags = 0
if not self.iscase():
flags = flags | re.IGNORECASE
try:
prog = re.compile(pat, flags)
except re.error, what:
try:
msg, col = what
except:
msg = str(what)
col = -1
self.report_error(pat, msg, col)
return None
return prog
def report_error(self, pat, msg, col=-1):
# Derived class could overrid this with something fancier
msg = "Error: " + str(msg)
if pat:
msg = msg + "\np\Pattern: " + str(pat)
if col >= 0:
msg = msg + "\nOffset: " + str(col)
tkMessageBox.showerror("Regular expression error",
msg, master=self.root)
def setcookedpat(self, pat):
if self.isre():
pat = re.escape(pat)
self.setpat(pat)
def search_text(self, text, prog=None, ok=0):
"""Search a text widget for the pattern.
If prog is given, it should be the precompiled pattern.
Return a tuple (lineno, matchobj); None if not found.
This obeys the wrap and direction (back) settings.
The search starts at the selection (if there is one) or
at the insert mark (otherwise). If the search is forward,
it starts at the right of the selection; for a backward
search, it starts at the left end. An empty match exactly
at either end of the selection (or at the insert mark if
there is no selection) is ignored unless the ok flag is true
-- this is done to guarantee progress.
If the search is allowed to wrap around, it will return the
original selection if (and only if) it is the only match.
"""
if not prog:
prog = self.getprog()
if not prog:
return None # Compilation failed -- stop
wrap = self.wrapvar.get()
first, last = get_selection(text)
if self.isback():
if ok:
start = last
else:
start = first
line, col = get_line_col(start)
res = self.search_backward(text, prog, line, col, wrap, ok)
else:
if ok:
start = first
else:
start = last
line, col = get_line_col(start)
res = self.search_forward(text, prog, line, col, wrap, ok)
return res
def search_forward(self, text, prog, line, col, wrap, ok=0):
wrapped = 0
startline = line
chars = text.get("%d.0" % line, "%d.0" % (line+1))
while chars:
m = prog.search(chars[:-1], col)
if m:
if ok or m.end() > col:
return line, m
line = line + 1
if wrapped and line > startline:
break
col = 0
ok = 1
chars = text.get("%d.0" % line, "%d.0" % (line+1))
if not chars and wrap:
wrapped = 1
wrap = 0
line = 1
chars = text.get("1.0", "2.0")
return None
def search_backward(self, text, prog, line, col, wrap, ok=0):
wrapped = 0
startline = line
chars = text.get("%d.0" % line, "%d.0" % (line+1))
while 1:
m = search_reverse(prog, chars[:-1], col)
if m:
if ok or m.start() < col:
return line, m
line = line - 1
if wrapped and line < startline:
break
ok = 1
if line <= 0:
if not wrap:
break
wrapped = 1
wrap = 0
pos = text.index("end-1c")
line, col = map(int, pos.split("."))
chars = text.get("%d.0" % line, "%d.0" % (line+1))
col = len(chars) - 1
return None
# Helper to search backwards in a string.
# (Optimized for the case where the pattern isn't found.)
def search_reverse(prog, chars, col):
m = prog.search(chars)
if not m:
return None
found = None
i, j = m.span()
while i < col and j <= col:
found = m
if i == j:
j = j+1
m = prog.search(chars, j)
if not m:
break
i, j = m.span()
return found
# Helper to get selection end points, defaulting to insert mark.
# Return a tuple of indices ("line.col" strings).
def get_selection(text):
try:
first = text.index("sel.first")
last = text.index("sel.last")
except TclError:
first = last = None
if not first:
first = text.index("insert")
if not last:
last = first
return first, last
# Helper to parse a text index into a (line, col) tuple.
def get_line_col(index):
line, col = map(int, index.split(".")) # Fails on invalid index
return line, col

View File

@ -1,92 +0,0 @@
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()

View File

@ -1,146 +0,0 @@
import os
import sys
import linecache
from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
from ObjectBrowser import ObjectTreeItem, make_objecttreeitem
from OldStackViewer import StackViewer, NamespaceViewer
def StackBrowser(root, flist=None, tb=None, top=None):
if top is None:
from Tkinter import Toplevel
top = Toplevel(root)
sc = ScrolledCanvas(top, bg="white", highlightthickness=0)
sc.frame.pack(expand=1, fill="both")
item = StackTreeItem(flist, tb)
node = TreeNode(sc.canvas, None, item)
node.expand()
class StackTreeItem(TreeItem):
def __init__(self, flist=None, tb=None):
self.flist = flist
self.stack = get_stack(tb)
self.text = get_exception()
def GetText(self):
return self.text
def GetSubList(self):
sublist = []
for info in self.stack:
item = FrameTreeItem(info, self.flist)
sublist.append(item)
return sublist
class FrameTreeItem(TreeItem):
def __init__(self, info, flist):
self.info = info
self.flist = flist
def GetText(self):
frame, lineno = self.info
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
return item
def GetSubList(self):
frame, lineno = self.info
sublist = []
if frame.f_globals is not frame.f_locals:
item = VariablesTreeItem("<locals>", frame.f_locals, self.flist)
sublist.append(item)
item = VariablesTreeItem("<globals>", frame.f_globals, self.flist)
sublist.append(item)
return sublist
def OnDoubleClick(self):
if self.flist:
frame, lineno = self.info
filename = frame.f_code.co_filename
if os.path.isfile(filename):
self.flist.gotofileline(filename, lineno)
class VariablesTreeItem(ObjectTreeItem):
def GetText(self):
return self.labeltext
def GetLabelText(self):
return None
def IsExpandable(self):
return len(self.object) > 0
def keys(self):
return self.object.keys()
def GetSubList(self):
sublist = []
for key in self.keys():
try:
value = self.object[key]
except KeyError:
continue
def setfunction(value, key=key, object=self.object):
object[key] = value
item = make_objecttreeitem(key + " =", value, setfunction)
sublist.append(item)
return sublist
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 get_exception(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
def _test():
try:
import testcode
reload(testcode)
except:
sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info()
from Tkinter import Tk
root = Tk()
StackBrowser(None, top=root)
root.mainloop()
if __name__ == "__main__":
_test()

View File

@ -1,211 +0,0 @@
TO DO:
- improve debugger:
- manage breakpoints globally, allow bp deletion, tbreak, cbreak etc.
- real object browser
- help on how to use it (a simple help button will do wonders)
- performance? (updates of large sets of locals are slow)
- better integration of "debug module"
- debugger should be global resource (attached to flist, not to shell)
- fix the stupid bug where you need to step twice
- display class name in stack viewer entries for methods
- suppress tracing through IDLE internals (e.g. print)
- add a button to suppress through a specific module or class or method
- more object inspection to stack viewer, e.g. to view all array items
- insert the initial current directory into sys.path
- default directory attribute for each window instead of only for windows
that have an associated filename
- command expansion from keywords, module contents, other buffers, etc.
- "Recent documents" menu item
- Filter region command
- Optional horizontal scroll bar
- more Emacsisms:
- ^K should cut to buffer
- M-[, M-] to move by paragraphs
- incremental search?
- search should indicate wrap-around in some way
- restructure state sensitive code to avoid testing flags all the time
- persistent user state (e.g. window and cursor positions, bindings)
- make backups when saving
- check file mtimes at various points
- Pluggable interface with RCS/CVS/Perforce/Clearcase
- better help?
- don't open second class browser on same module (nor second path browser)
- unify class and path browsers
- Need to define a standard way whereby one can determine one is running
inside IDLE (needed for Tk mainloop, also handy for $PYTHONSTARTUP)
- Add more utility methods for use by extensions (a la get_selection)
- Way to run command in totally separate interpreter (fork+os.system?)
- Way to find definition of fully-qualified name:
In other words, select "UserDict.UserDict", hit some magic key and
it loads up UserDict.py and finds the first def or class for UserDict.
- need a way to force colorization on/off
- need a way to force auto-indent on/off
Details:
- when there's a selection, left/right arrow should go to either
end of the selection
- ^O (on Unix -- open-line) should honor autoindent
- after paste, show end of pasted text
- on Windows, should turn short filename to long filename (not only in argv!)
(shouldn't this be done -- or undone -- by ntpath.normpath?)
- new autoindent after colon even indents when the colon is in a comment!
- sometimes forward slashes in pathname remain
- sometimes star in window name remains in Windows menu
- With unix bindings, ESC by itself is ignored
- Sometimes for no apparent reason a selection from the cursor to the
end of the command buffer appears, which is hard to get rid of
because it stays when you are typing!
- The Line/Col in the status bar can be wrong initially in PyShell
Structural problems:
- too much knowledge in FileList about EditorWindow (for example)
- should add some primitives for accessing the selection etc.
to repeat cumbersome code over and over
======================================================================
Jeff Bauer suggests:
- Open Module doesn't appear to handle hierarchical packages.
- Class browser should also allow hierarchical packages.
- Open and Open Module could benefit from a history,
either command line style, or Microsoft recent-file
style.
- Add a Smalltalk-style inspector (i.e. Tkinspect)
The last suggestion is already a reality, but not yet
integrated into IDLE. I use a module called inspector.py,
that used to be available from python.org(?) It no longer
appears to be in the contributed section, and the source
has no author attribution.
In any case, the code is useful for visually navigating
an object's attributes, including its container hierarchy.
>>> from inspector import Tkinspect
>>> Tkinspect(None, myObject)
Tkinspect could probably be extended and refined to
integrate better into IDLE.
======================================================================
Comparison to PTUI
------------------
+ PTUI's help is better (HTML!)
+ PTUI can attach a shell to any module
+ PTUI has some more I/O commands:
open multiple
append
examine (what's that?)
======================================================================
Notes after trying to run Grail
-------------------------------
- Grail does stuff to sys.path based on sys.argv[0]; you must set
sys.argv[0] to something decent first (it is normally set to the path of
the idle script).
- Grail must be exec'ed in __main__ because that's imported by some
other parts of Grail.
- Grail uses a module called History and so does idle :-(
======================================================================
Robin Friedrich's items:
Things I'd like to see:
- I'd like support for shift-click extending the selection. There's a
bug now that it doesn't work the first time you try it.
- Printing is needed. How hard can that be on Windows?
- The python-mode trick of autoindenting a line with <tab> is neat and
very handy.
- (someday) a spellchecker for docstrings and comments.
- a pagedown/up command key which moves to next class/def statement (top
level)
- split window capability
- DnD text relocation/copying
Things I don't want to see.
- line numbers... will probably slow things down way too much.
- Please use another icon for the tree browser leaf. The small snake
isn't cutting it.
----------------------------------------------------------------------
- Customizable views (multi-window or multi-pane). (Markus Gritsch)
- Being able to double click (maybe double right click) on a callable
object in the editor which shows the source of the object, if
possible. (Gerrit Holl)
- Hooks into the guts, like in Emacs. (Mike Romberg)
- Sharing the editor with a remote tutor. (Martijn Faassen)
- Multiple views on the same file. (Tony J Ibbs)
- Store breakpoints in a global (per-project) database (GvR); Dirk
Heise adds: save some space-trimmed context and search around when
reopening a file that might have been edited by someone else.
- Capture menu events in extensions without changing the IDLE source.
(Matthias Barmeier)
- Use overlapping panels (a "notebook" in MFC terms I think) for info
that doesn't need to be accessible simultaneously (e.g. HTML source
and output). Use multi-pane windows for info that does need to be
shown together (e.g. class browser and source). (Albert Brandl)
- A project should invisibly track all symbols, for instant search,
replace and cross-ref. Projects should be allowed to span multiple
directories, hosts, etc. Project management files are placed in a
directory you specify. A global mapping between project names and
project directories should exist [not so sure --GvR]. (Tim Peters)
- Merge attr-tips and auto-expand. (Mark Hammond, Tim Peters)
- Python Shell should behave more like a "shell window" as users know
it -- i.e. you can only edit the current command, and the cursor can't
escape from the command area. (Albert Brandl)
- Set X11 class to "idle/Idle", set icon and title to something
beginning with "idle" -- for window manangers. (Randall Hopper)
- Config files editable through a preferences dialog. (me)
- Config files still editable outside the preferences dialog.
(Randall Hopper)
- When you're editing a command in PyShell, and there are only blank
lines below the cursor, hitting Return should ignore or delete those
blank lines rather than deciding you're not on the last line. (me)
- Run command (F5 c.s.) should be more like Pythonwin's Run -- a
dialog with options to give command line arguments, run the debugger,
etc. (me)
- Shouldn't be able to delete part of the prompt (or any text before
it) in the PyShell. (Martijn Faassen)
- Emacs style auto-fill (also smart about comments and strings).
(Jeremy Hylton)
- Output of Run Script should go to a separate output window, not to
the shell window. Output of separate runs should all go to the same
window but clearly delimited. (David Scherer)
- GUI form designer to kick VB's butt. (Robert Geiger)
- Printing! Possibly via generation of PDF files which the user must
then send to the printer separately. (Dinu Gherman)

View File

@ -1,86 +0,0 @@
# Ideas gleaned from PySol
from Tkinter import *
class ToolTipBase:
def __init__(self, button):
self.button = button
self.tipwindow = None
self.id = None
self.x = self.y = 0
self._id1 = self.button.bind("<Enter>", self.enter)
self._id2 = self.button.bind("<Leave>", self.leave)
self._id3 = self.button.bind("<ButtonPress>", self.leave)
def enter(self, event=None):
self.schedule()
def leave(self, event=None):
self.unschedule()
self.hidetip()
def schedule(self):
self.unschedule()
self.id = self.button.after(1500, self.showtip)
def unschedule(self):
id = self.id
self.id = None
if id:
self.button.after_cancel(id)
def showtip(self):
if self.tipwindow:
return
# The tip window must be completely outside the button;
# otherwise when the mouse enters the tip window we get
# a leave event and it disappears, and then we get an enter
# event and it reappears, and so on forever :-(
x = self.button.winfo_rootx() + 20
y = self.button.winfo_rooty() + self.button.winfo_height() + 1
self.tipwindow = tw = Toplevel(self.button)
tw.wm_overrideredirect(1)
tw.wm_geometry("+%d+%d" % (x, y))
self.showcontents()
def showcontents(self, text="Your text here"):
# Override this in derived class
label = Label(self.tipwindow, text=text, justify=LEFT,
background="#ffffe0", relief=SOLID, borderwidth=1)
label.pack()
def hidetip(self):
tw = self.tipwindow
self.tipwindow = None
if tw:
tw.destroy()
class ToolTip(ToolTipBase):
def __init__(self, button, text):
ToolTipBase.__init__(self, button)
self.text = text
def showcontents(self):
ToolTipBase.showcontents(self, self.text)
class ListboxToolTip(ToolTipBase):
def __init__(self, button, items):
ToolTipBase.__init__(self, button)
self.items = items
def showcontents(self):
listbox = Listbox(self.tipwindow, background="#ffffe0")
listbox.pack()
for item in self.items:
listbox.insert(END, item)
def main():
# Test code
root = Tk()
b = Button(root, text="Hello", command=root.destroy)
b.pack()
root.update()
tip = ListboxToolTip(b, ["Hello", "world"])
# root.mainloop() # not in idle
main()

View File

@ -1,476 +0,0 @@
# XXX TO DO:
# - popup menu
# - support partial or total redisplay
# - key bindings (instead of quick-n-dirty bindings on Canvas):
# - up/down arrow keys to move focus around
# - ditto for page up/down, home/end
# - left/right arrows to expand/collapse & move out/in
# - more doc strings
# - add icons for "file", "module", "class", "method"; better "python" icon
# - callback for selection???
# - multiple-item selection
# - tooltips
# - redo geometry without magic numbers
# - keep track of object ids to allow more careful cleaning
# - optimize tree redraw after expand of subnode
import os
import sys
from Tkinter import *
import imp
import ZoomHeight
ICONDIR = "Icons"
# Look for Icons subdirectory in the same directory as this module
try:
_icondir = os.path.join(os.path.dirname(__file__), ICONDIR)
except NameError:
_icondir = ICONDIR
if os.path.isdir(_icondir):
ICONDIR = _icondir
elif not os.path.isdir(ICONDIR):
raise RuntimeError, "can't find icon directory (%s)" % `ICONDIR`
def listicons(icondir=ICONDIR):
"""Utility to display the available icons."""
root = Tk()
import glob
list = glob.glob(os.path.join(icondir, "*.gif"))
list.sort()
images = []
row = column = 0
for file in list:
name = os.path.splitext(os.path.basename(file))[0]
image = PhotoImage(file=file, master=root)
images.append(image)
label = Label(root, image=image, bd=1, relief="raised")
label.grid(row=row, column=column)
label = Label(root, text=name)
label.grid(row=row+1, column=column)
column = column + 1
if column >= 10:
row = row+2
column = 0
root.images = images
class TreeNode:
def __init__(self, canvas, parent, item):
self.canvas = canvas
self.parent = parent
self.item = item
self.state = 'collapsed'
self.selected = 0
self.children = []
self.x = self.y = None
self.iconimages = {} # cache of PhotoImage instances for icons
def destroy(self):
for c in self.children[:]:
self.children.remove(c)
c.destroy()
self.parent = None
def geticonimage(self, name):
try:
return self.iconimages[name]
except KeyError:
pass
file, ext = os.path.splitext(name)
ext = ext or ".gif"
fullname = os.path.join(ICONDIR, file + ext)
image = PhotoImage(master=self.canvas, file=fullname)
self.iconimages[name] = image
return image
def select(self, event=None):
if self.selected:
return
self.deselectall()
self.selected = 1
self.canvas.delete(self.image_id)
self.drawicon()
self.drawtext()
def deselect(self, event=None):
if not self.selected:
return
self.selected = 0
self.canvas.delete(self.image_id)
self.drawicon()
self.drawtext()
def deselectall(self):
if self.parent:
self.parent.deselectall()
else:
self.deselecttree()
def deselecttree(self):
if self.selected:
self.deselect()
for child in self.children:
child.deselecttree()
def flip(self, event=None):
if self.state == 'expanded':
self.collapse()
else:
self.expand()
self.item.OnDoubleClick()
return "break"
def expand(self, event=None):
if not self.item._IsExpandable():
return
if self.state != 'expanded':
self.state = 'expanded'
self.update()
self.view()
def collapse(self, event=None):
if self.state != 'collapsed':
self.state = 'collapsed'
self.update()
def view(self):
top = self.y - 2
bottom = self.lastvisiblechild().y + 17
height = bottom - top
visible_top = self.canvas.canvasy(0)
visible_height = self.canvas.winfo_height()
visible_bottom = self.canvas.canvasy(visible_height)
if visible_top <= top and bottom <= visible_bottom:
return
x0, y0, x1, y1 = self.canvas._getints(self.canvas['scrollregion'])
if top >= visible_top and height <= visible_height:
fraction = top + height - visible_height
else:
fraction = top
fraction = float(fraction) / y1
self.canvas.yview_moveto(fraction)
def lastvisiblechild(self):
if self.children and self.state == 'expanded':
return self.children[-1].lastvisiblechild()
else:
return self
def update(self):
if self.parent:
self.parent.update()
else:
oldcursor = self.canvas['cursor']
self.canvas['cursor'] = "watch"
self.canvas.update()
self.canvas.delete(ALL) # XXX could be more subtle
self.draw(7, 2)
x0, y0, x1, y1 = self.canvas.bbox(ALL)
self.canvas.configure(scrollregion=(0, 0, x1, y1))
self.canvas['cursor'] = oldcursor
def draw(self, x, y):
# XXX This hard-codes too many geometry constants!
self.x, self.y = x, y
self.drawicon()
self.drawtext()
if self.state != 'expanded':
return y+17
# draw children
if not self.children:
sublist = self.item._GetSubList()
if not sublist:
# _IsExpandable() was mistaken; that's allowed
return y+17
for item in sublist:
child = self.__class__(self.canvas, self, item)
self.children.append(child)
cx = x+20
cy = y+17
cylast = 0
for child in self.children:
cylast = cy
self.canvas.create_line(x+9, cy+7, cx, cy+7, fill="gray50")
cy = child.draw(cx, cy)
if child.item._IsExpandable():
if child.state == 'expanded':
iconname = "minusnode"
callback = child.collapse
else:
iconname = "plusnode"
callback = child.expand
image = self.geticonimage(iconname)
id = self.canvas.create_image(x+9, cylast+7, image=image)
# XXX This leaks bindings until canvas is deleted:
self.canvas.tag_bind(id, "<1>", callback)
self.canvas.tag_bind(id, "<Double-1>", lambda x: None)
id = self.canvas.create_line(x+9, y+10, x+9, cylast+7,
##stipple="gray50", # XXX Seems broken in Tk 8.0.x
fill="gray50")
self.canvas.tag_lower(id) # XXX .lower(id) before Python 1.5.2
return cy
def drawicon(self):
if self.selected:
imagename = (self.item.GetSelectedIconName() or
self.item.GetIconName() or
"openfolder")
else:
imagename = self.item.GetIconName() or "folder"
image = self.geticonimage(imagename)
id = self.canvas.create_image(self.x, self.y, anchor="nw", image=image)
self.image_id = id
self.canvas.tag_bind(id, "<1>", self.select)
self.canvas.tag_bind(id, "<Double-1>", self.flip)
def drawtext(self):
textx = self.x+20-1
texty = self.y-1
labeltext = self.item.GetLabelText()
if labeltext:
id = self.canvas.create_text(textx, texty, anchor="nw",
text=labeltext)
self.canvas.tag_bind(id, "<1>", self.select)
self.canvas.tag_bind(id, "<Double-1>", self.flip)
x0, y0, x1, y1 = self.canvas.bbox(id)
textx = max(x1, 200) + 10
text = self.item.GetText() or "<no text>"
try:
self.entry
except AttributeError:
pass
else:
self.edit_finish()
try:
label = self.label
except AttributeError:
# padding carefully selected (on Windows) to match Entry widget:
self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2)
if self.selected:
self.label.configure(fg="white", bg="darkblue")
else:
self.label.configure(fg="black", bg="white")
id = self.canvas.create_window(textx, texty,
anchor="nw", window=self.label)
self.label.bind("<1>", self.select_or_edit)
self.label.bind("<Double-1>", self.flip)
self.text_id = id
def select_or_edit(self, event=None):
if self.selected and self.item.IsEditable():
self.edit(event)
else:
self.select(event)
def edit(self, event=None):
self.entry = Entry(self.label, bd=0, highlightthickness=1, width=0)
self.entry.insert(0, self.label['text'])
self.entry.selection_range(0, END)
self.entry.pack(ipadx=5)
self.entry.focus_set()
self.entry.bind("<Return>", self.edit_finish)
self.entry.bind("<Escape>", self.edit_cancel)
def edit_finish(self, event=None):
try:
entry = self.entry
del self.entry
except AttributeError:
return
text = entry.get()
entry.destroy()
if text and text != self.item.GetText():
self.item.SetText(text)
text = self.item.GetText()
self.label['text'] = text
self.drawtext()
self.canvas.focus_set()
def edit_cancel(self, event=None):
try:
entry = self.entry
del self.entry
except AttributeError:
return
entry.destroy()
self.drawtext()
self.canvas.focus_set()
class TreeItem:
"""Abstract class representing tree items.
Methods should typically be overridden, otherwise a default action
is used.
"""
def __init__(self):
"""Constructor. Do whatever you need to do."""
def GetText(self):
"""Return text string to display."""
def GetLabelText(self):
"""Return label text string to display in front of text (if any)."""
expandable = None
def _IsExpandable(self):
"""Do not override! Called by TreeNode."""
if self.expandable is None:
self.expandable = self.IsExpandable()
return self.expandable
def IsExpandable(self):
"""Return whether there are subitems."""
return 1
def _GetSubList(self):
"""Do not override! Called by TreeNode."""
if not self.IsExpandable():
return []
sublist = self.GetSubList()
if not sublist:
self.expandable = 0
return sublist
def IsEditable(self):
"""Return whether the item's text may be edited."""
def SetText(self, text):
"""Change the item's text (if it is editable)."""
def GetIconName(self):
"""Return name of icon to be displayed normally."""
def GetSelectedIconName(self):
"""Return name of icon to be displayed when selected."""
def GetSubList(self):
"""Return list of items forming sublist."""
def OnDoubleClick(self):
"""Called on a double-click on the item."""
# Example application
class FileTreeItem(TreeItem):
"""Example TreeItem subclass -- browse the file system."""
def __init__(self, path):
self.path = path
def GetText(self):
return os.path.basename(self.path) or self.path
def IsEditable(self):
return os.path.basename(self.path) != ""
def SetText(self, text):
newpath = os.path.dirname(self.path)
newpath = os.path.join(newpath, text)
if os.path.dirname(newpath) != os.path.dirname(self.path):
return
try:
os.rename(self.path, newpath)
self.path = newpath
except os.error:
pass
def GetIconName(self):
if not self.IsExpandable():
return "python" # XXX wish there was a "file" icon
def IsExpandable(self):
return os.path.isdir(self.path)
def GetSubList(self):
try:
names = os.listdir(self.path)
except os.error:
return []
names.sort(lambda a, b: cmp(os.path.normcase(a), os.path.normcase(b)))
sublist = []
for name in names:
item = FileTreeItem(os.path.join(self.path, name))
sublist.append(item)
return sublist
# A canvas widget with scroll bars and some useful bindings
class ScrolledCanvas:
def __init__(self, master, **opts):
if not opts.has_key('yscrollincrement'):
opts['yscrollincrement'] = 17
self.master = master
self.frame = Frame(master)
self.frame.rowconfigure(0, weight=1)
self.frame.columnconfigure(0, weight=1)
self.canvas = apply(Canvas, (self.frame,), opts)
self.canvas.grid(row=0, column=0, sticky="nsew")
self.vbar = Scrollbar(self.frame, name="vbar")
self.vbar.grid(row=0, column=1, sticky="nse")
self.hbar = Scrollbar(self.frame, name="hbar", orient="horizontal")
self.hbar.grid(row=1, column=0, sticky="ews")
self.canvas['yscrollcommand'] = self.vbar.set
self.vbar['command'] = self.canvas.yview
self.canvas['xscrollcommand'] = self.hbar.set
self.hbar['command'] = self.canvas.xview
self.canvas.bind("<Key-Prior>", self.page_up)
self.canvas.bind("<Key-Next>", self.page_down)
self.canvas.bind("<Key-Up>", self.unit_up)
self.canvas.bind("<Key-Down>", self.unit_down)
if isinstance(master, Toplevel) or isinstance(master, Tk):
self.canvas.bind("<Alt-F2>", self.zoom_height)
self.canvas.focus_set()
def page_up(self, event):
self.canvas.yview_scroll(-1, "page")
return "break"
def page_down(self, event):
self.canvas.yview_scroll(1, "page")
return "break"
def unit_up(self, event):
self.canvas.yview_scroll(-1, "unit")
return "break"
def unit_down(self, event):
self.canvas.yview_scroll(1, "unit")
return "break"
def zoom_height(self, event):
ZoomHeight.zoom_height(self.master)
return "break"
# Testing functions
def test():
import PyShell
root = Toplevel(PyShell.root)
root.configure(bd=0, bg="yellow")
root.focus_set()
sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1)
sc.frame.pack(expand=1, fill="both")
item = FileTreeItem("C:/windows/desktop")
node = TreeNode(sc.canvas, None, item)
node.expand()
def test2():
# test w/o scrolling canvas
root = Tk()
root.configure(bd=0)
canvas = Canvas(root, bg="white", highlightthickness=0)
canvas.pack(expand=1, fill="both")
item = FileTreeItem(os.curdir)
node = TreeNode(canvas, None, item)
node.update()
canvas.focus_set()
if __name__ == '__main__':
test()

View File

@ -1,352 +0,0 @@
import sys
import string
from Tkinter import *
from Delegator import Delegator
#$ event <<redo>>
#$ win <Control-y>
#$ unix <Alt-z>
#$ event <<undo>>
#$ win <Control-z>
#$ unix <Control-z>
#$ event <<dump-undo-state>>
#$ win <Control-backslash>
#$ unix <Control-backslash>
class UndoDelegator(Delegator):
max_undo = 1000
def __init__(self):
Delegator.__init__(self)
self.reset_undo()
def setdelegate(self, delegate):
if self.delegate is not None:
self.unbind("<<undo>>")
self.unbind("<<redo>>")
self.unbind("<<dump-undo-state>>")
Delegator.setdelegate(self, delegate)
if delegate is not None:
self.bind("<<undo>>", self.undo_event)
self.bind("<<redo>>", self.redo_event)
self.bind("<<dump-undo-state>>", self.dump_event)
def dump_event(self, event):
from pprint import pprint
pprint(self.undolist[:self.pointer])
print "pointer:", self.pointer,
print "saved:", self.saved,
print "can_merge:", self.can_merge,
print "get_saved():", self.get_saved()
pprint(self.undolist[self.pointer:])
return "break"
def reset_undo(self):
self.was_saved = -1
self.pointer = 0
self.undolist = []
self.undoblock = 0 # or a CommandSequence instance
self.set_saved(1)
def set_saved(self, flag):
if flag:
self.saved = self.pointer
else:
self.saved = -1
self.can_merge = 0
self.check_saved()
def get_saved(self):
return self.saved == self.pointer
saved_change_hook = None
def set_saved_change_hook(self, hook):
self.saved_change_hook = hook
was_saved = -1
def check_saved(self):
is_saved = self.get_saved()
if is_saved != self.was_saved:
self.was_saved = is_saved
if self.saved_change_hook:
self.saved_change_hook()
def insert(self, index, chars, tags=None):
self.addcmd(InsertCommand(index, chars, tags))
def delete(self, index1, index2=None):
self.addcmd(DeleteCommand(index1, index2))
# Clients should call undo_block_start() and undo_block_stop()
# around a sequence of editing cmds to be treated as a unit by
# undo & redo. Nested matching calls are OK, and the inner calls
# then act like nops. OK too if no editing cmds, or only one
# editing cmd, is issued in between: if no cmds, the whole
# sequence has no effect; and if only one cmd, that cmd is entered
# directly into the undo list, as if undo_block_xxx hadn't been
# called. The intent of all that is to make this scheme easy
# to use: all the client has to worry about is making sure each
# _start() call is matched by a _stop() call.
def undo_block_start(self):
if self.undoblock == 0:
self.undoblock = CommandSequence()
self.undoblock.bump_depth()
def undo_block_stop(self):
if self.undoblock.bump_depth(-1) == 0:
cmd = self.undoblock
self.undoblock = 0
if len(cmd) > 0:
if len(cmd) == 1:
# no need to wrap a single cmd
cmd = cmd.getcmd(0)
# this blk of cmds, or single cmd, has already
# been done, so don't execute it again
self.addcmd(cmd, 0)
def addcmd(self, cmd, execute=1):
if execute:
cmd.do(self.delegate)
if self.undoblock != 0:
self.undoblock.append(cmd)
return
if self.can_merge and self.pointer > 0:
lastcmd = self.undolist[self.pointer-1]
if lastcmd.merge(cmd):
return
self.undolist[self.pointer:] = [cmd]
if self.saved > self.pointer:
self.saved = -1
self.pointer = self.pointer + 1
if len(self.undolist) > self.max_undo:
##print "truncating undo list"
del self.undolist[0]
self.pointer = self.pointer - 1
if self.saved >= 0:
self.saved = self.saved - 1
self.can_merge = 1
self.check_saved()
def undo_event(self, event):
if self.pointer == 0:
self.bell()
return "break"
cmd = self.undolist[self.pointer - 1]
cmd.undo(self.delegate)
self.pointer = self.pointer - 1
self.can_merge = 0
self.check_saved()
return "break"
def redo_event(self, event):
if self.pointer >= len(self.undolist):
self.bell()
return "break"
cmd = self.undolist[self.pointer]
cmd.redo(self.delegate)
self.pointer = self.pointer + 1
self.can_merge = 0
self.check_saved()
return "break"
class Command:
# Base class for Undoable commands
tags = None
def __init__(self, index1, index2, chars, tags=None):
self.marks_before = {}
self.marks_after = {}
self.index1 = index1
self.index2 = index2
self.chars = chars
if tags:
self.tags = tags
def __repr__(self):
s = self.__class__.__name__
t = (self.index1, self.index2, self.chars, self.tags)
if self.tags is None:
t = t[:-1]
return s + `t`
def do(self, text):
pass
def redo(self, text):
pass
def undo(self, text):
pass
def merge(self, cmd):
return 0
def save_marks(self, text):
marks = {}
for name in text.mark_names():
if name != "insert" and name != "current":
marks[name] = text.index(name)
return marks
def set_marks(self, text, marks):
for name, index in marks.items():
text.mark_set(name, index)
class InsertCommand(Command):
# Undoable insert command
def __init__(self, index1, chars, tags=None):
Command.__init__(self, index1, None, chars, tags)
def do(self, text):
self.marks_before = self.save_marks(text)
self.index1 = text.index(self.index1)
if text.compare(self.index1, ">", "end-1c"):
# Insert before the final newline
self.index1 = text.index("end-1c")
text.insert(self.index1, self.chars, self.tags)
self.index2 = text.index("%s+%dc" % (self.index1, len(self.chars)))
self.marks_after = self.save_marks(text)
##sys.__stderr__.write("do: %s\n" % self)
def redo(self, text):
text.mark_set('insert', self.index1)
text.insert(self.index1, self.chars, self.tags)
self.set_marks(text, self.marks_after)
text.see('insert')
##sys.__stderr__.write("redo: %s\n" % self)
def undo(self, text):
text.mark_set('insert', self.index1)
text.delete(self.index1, self.index2)
self.set_marks(text, self.marks_before)
text.see('insert')
##sys.__stderr__.write("undo: %s\n" % self)
def merge(self, cmd):
if self.__class__ is not cmd.__class__:
return 0
if self.index2 != cmd.index1:
return 0
if self.tags != cmd.tags:
return 0
if len(cmd.chars) != 1:
return 0
if self.chars and \
self.classify(self.chars[-1]) != self.classify(cmd.chars):
return 0
self.index2 = cmd.index2
self.chars = self.chars + cmd.chars
return 1
alphanumeric = string.ascii_letters + string.digits + "_"
def classify(self, c):
if c in self.alphanumeric:
return "alphanumeric"
if c == "\n":
return "newline"
return "punctuation"
class DeleteCommand(Command):
# Undoable delete command
def __init__(self, index1, index2=None):
Command.__init__(self, index1, index2, None, None)
def do(self, text):
self.marks_before = self.save_marks(text)
self.index1 = text.index(self.index1)
if self.index2:
self.index2 = text.index(self.index2)
else:
self.index2 = text.index(self.index1 + " +1c")
if text.compare(self.index2, ">", "end-1c"):
# Don't delete the final newline
self.index2 = text.index("end-1c")
self.chars = text.get(self.index1, self.index2)
text.delete(self.index1, self.index2)
self.marks_after = self.save_marks(text)
##sys.__stderr__.write("do: %s\n" % self)
def redo(self, text):
text.mark_set('insert', self.index1)
text.delete(self.index1, self.index2)
self.set_marks(text, self.marks_after)
text.see('insert')
##sys.__stderr__.write("redo: %s\n" % self)
def undo(self, text):
text.mark_set('insert', self.index1)
text.insert(self.index1, self.chars)
self.set_marks(text, self.marks_before)
text.see('insert')
##sys.__stderr__.write("undo: %s\n" % self)
class CommandSequence(Command):
# Wrapper for a sequence of undoable cmds to be undone/redone
# as a unit
def __init__(self):
self.cmds = []
self.depth = 0
def __repr__(self):
s = self.__class__.__name__
strs = []
for cmd in self.cmds:
strs.append(" " + `cmd`)
return s + "(\n" + ",\n".join(strs) + "\n)"
def __len__(self):
return len(self.cmds)
def append(self, cmd):
self.cmds.append(cmd)
def getcmd(self, i):
return self.cmds[i]
def redo(self, text):
for cmd in self.cmds:
cmd.redo(text)
def undo(self, text):
cmds = self.cmds[:]
cmds.reverse()
for cmd in cmds:
cmd.undo(text)
def bump_depth(self, incr=1):
self.depth = self.depth + incr
return self.depth
def main():
from Percolator import Percolator
root = Tk()
root.wm_protocol("WM_DELETE_WINDOW", root.quit)
text = Text()
text.pack()
text.focus_set()
p = Percolator(text)
d = UndoDelegator()
p.insertfilter(d)
root.mainloop()
if __name__ == "__main__":
main()

View File

@ -1,92 +0,0 @@
from Tkinter import *
class WidgetRedirector:
"""Support for redirecting arbitrary widget subcommands."""
def __init__(self, widget):
self.dict = {}
self.widget = widget
self.tk = tk = widget.tk
w = widget._w
self.orig = w + "_orig"
tk.call("rename", w, self.orig)
tk.createcommand(w, self.dispatch)
def __repr__(self):
return "WidgetRedirector(%s<%s>)" % (self.widget.__class__.__name__,
self.widget._w)
def close(self):
for name in self.dict.keys():
self.unregister(name)
widget = self.widget; del self.widget
orig = self.orig; del self.orig
tk = widget.tk
w = widget._w
tk.deletecommand(w)
tk.call("rename", orig, w)
def register(self, name, function):
if self.dict.has_key(name):
previous = dict[name]
else:
previous = OriginalCommand(self, name)
self.dict[name] = function
setattr(self.widget, name, function)
return previous
def unregister(self, name):
if self.dict.has_key(name):
function = self.dict[name]
del self.dict[name]
if hasattr(self.widget, name):
delattr(self.widget, name)
return function
else:
return None
def dispatch(self, cmd, *args):
m = self.dict.get(cmd)
try:
if m:
return apply(m, args)
else:
return self.tk.call((self.orig, cmd) + args)
except TclError:
return ""
class OriginalCommand:
def __init__(self, redir, name):
self.redir = redir
self.name = name
self.tk = redir.tk
self.orig = redir.orig
self.tk_call = self.tk.call
self.orig_and_name = (self.orig, self.name)
def __repr__(self):
return "OriginalCommand(%s, %s)" % (`self.redir`, `self.name`)
def __call__(self, *args):
return self.tk_call(self.orig_and_name + args)
def main():
root = Tk()
text = Text()
text.pack()
text.focus_set()
redir = WidgetRedirector(text)
global orig_insert
def my_insert(*args):
print "insert", args
apply(orig_insert, args)
orig_insert = redir.register("insert", my_insert)
root.mainloop()
if __name__ == "__main__":
main()

View File

@ -1,85 +0,0 @@
from Tkinter import *
class WindowList:
def __init__(self):
self.dict = {}
self.callbacks = []
def add(self, window):
window.after_idle(self.call_callbacks)
self.dict[str(window)] = window
def delete(self, window):
try:
del self.dict[str(window)]
except KeyError:
# Sometimes, destroy() is called twice
pass
self.call_callbacks()
def add_windows_to_menu(self, menu):
list = []
for key in self.dict.keys():
window = self.dict[key]
try:
title = window.get_title()
except TclError:
continue
list.append((title, window))
list.sort()
for title, window in list:
if title == "Python Shell":
# Hack -- until we have a better way to this
continue
menu.add_command(label=title, command=window.wakeup)
def register_callback(self, callback):
self.callbacks.append(callback)
def unregister_callback(self, callback):
try:
self.callbacks.remove(callback)
except ValueError:
pass
def call_callbacks(self):
for callback in self.callbacks:
try:
callback()
except:
print "warning: callback failed in WindowList", \
sys.exc_type, ":", sys.exc_value
registry = WindowList()
add_windows_to_menu = registry.add_windows_to_menu
register_callback = registry.register_callback
unregister_callback = registry.unregister_callback
class ListedToplevel(Toplevel):
def __init__(self, master, **kw):
Toplevel.__init__(self, master, kw)
registry.add(self)
def destroy(self):
registry.delete(self)
Toplevel.destroy(self)
def get_title(self):
# Subclass can override
return self.wm_title()
def wakeup(self):
try:
if self.wm_state() == "iconic":
self.wm_deiconify()
else:
self.tkraise()
self.focus_set()
except TclError:
# This can happen when the window menu was torn off.
# Simply ignore it.
pass

View File

@ -1,46 +0,0 @@
# Sample extension: zoom a window to maximum height
import re
import sys
class ZoomHeight:
menudefs = [
('windows', [
('_Zoom Height', '<<zoom-height>>'),
])
]
windows_keydefs = {
'<<zoom-height>>': ['<Alt-F2>'],
}
unix_keydefs = {
'<<zoom-height>>': ['<Control-x><Control-z>'],
}
def __init__(self, editwin):
self.editwin = editwin
def zoom_height_event(self, event):
top = self.editwin.top
zoom_height(top)
def zoom_height(top):
geom = top.wm_geometry()
m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
if not m:
top.bell()
return
width, height, x, y = map(int, m.groups())
newheight = top.winfo_screenheight()
if sys.platform == 'win32':
newy = 0
newheight = newheight - 72
else:
newy = 24
newheight = newheight - 96
if height >= newheight:
newgeom = ""
else:
newgeom = "%dx%d+%d+%d" % (width, newheight, x, newy)
top.wm_geometry(newgeom)

View File

@ -1 +0,0 @@
# Dummy file to make this a potential package.

View File

@ -1,3 +0,0 @@
[EditorWindow]
font-name= monaco
font-size= 9

View File

@ -1,4 +0,0 @@
[EditorWindow]
font-name= courier
font-size= 10
print-command=lpr %s

View File

@ -1,4 +0,0 @@
[EditorWindow]
font-name: courier new
font-size: 10
print-command=start /min notepad /p %s

View File

@ -1,64 +0,0 @@
# 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

View File

@ -1,89 +0,0 @@
#! /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())

View File

@ -1,120 +0,0 @@
Writing an IDLE extension
An IDLE extension can define new key bindings and menu entries for IDLE
edit windows. There is a simple mechanism to load extensions when IDLE
starts up and to attach them to each edit window. (It is also possible
to make other changes to IDLE, but this must be done by editing the IDLE
source code.)
The list of extensions loaded at startup time is configured by editing
the file config.txt; see below for details.
An IDLE extension is defined by a class. Methods of the class define
actions that are invoked by those bindings or menu entries. Class (or
instance) variables define the bindings and menu additions; these are
automatically applied by IDLE when the extension is linked to an edit
window.
An IDLE extension class is instantiated with a single argument,
`editwin', an EditorWindow instance. The extension cannot assume much
about this argument, but it is guarateed to have the following instance
variables:
text a Text instance (a widget)
io an IOBinding instance (more about this later)
flist the FileList instance (shared by all edit windows)
(There are a few more, but they are rarely useful.)
The extension class must not bind key events. Rather, it must define
one or more virtual events, e.g. <<zoom-height>>, and corresponding
methods, e.g. zoom_height_event(), and have one or more class (or instance)
variables that define mappings between virtual events and key sequences,
e.g. <Alt-F2>. When the extension is loaded, these key sequences will
be bound to the corresponding virtual events, and the virtual events
will be bound to the corresponding methods. (This indirection is done
so that the key bindings can easily be changed, and so that other
sources of virtual events can exist, such as menu entries.)
The following class or instance variables are used to define key
bindings for virtual events:
keydefs for all platforms
mac_keydefs for Macintosh
windows_keydefs for Windows
unix_keydefs for Unix (and other platforms)
Each of these variables, if it exists, must be a dictionary whose
keys are virtual events, and whose values are lists of key sequences.
An extension can define menu entries in a similar fashion. This is done
with a class or instance variable named menudefs; it should be a list of
pair, where each pair is a menu name (lowercase) and a list of menu
entries. Each menu entry is either None (to insert a separator entry) or
a pair of strings (menu_label, virtual_event). Here, menu_label is the
label of the menu entry, and virtual_event is the virtual event to be
generated when the entry is selected. An underscore in the menu label
is removed; the character following the underscore is displayed
underlined, to indicate the shortcut character (for Windows).
At the moment, extensions cannot define whole new menus; they must
define entries in existing menus. Some menus are not present on some
windows; such entry definitions are then ignored, but the key bindings
are still applied. (This should probably be refined in the future.)
Here is a complete example example:
class ZoomHeight:
menudefs = [
('edit', [
None, # Separator
('_Zoom Height', '<<zoom-height>>'),
])
]
windows_keydefs = {
'<<zoom-height>>': ['<Alt-F2>'],
}
unix_keydefs = {
'<<zoom-height>>': ['<Control-z><Control-z>'],
}
def __init__(self, editwin):
self.editwin = editwin
def zoom_height_event(self, event):
"...Do what you want here..."
The final piece of the puzzle is the file "config.txt", which is used
to to configure the loading of extensions. For each extension,
you must include a section in config.txt (or in any of the other
configuration files that are consulted at startup: config-unix.txt,
config-win.txt, or ~/.idle). A section is headed by the module name
in square brackets, e.g.
[ZoomHeight]
The section may be empty, or it may define configuration options for
the extension. (See ParenMatch.py for an example.) A special option
is 'enable': including
enable = 0
in a section disables that extension. More than one configuration
file may specify options for the same extension, so a user may disable
an extension that is loaded by default, or enable an extension that is
disabled by default.
Extensions can define key bindings and menu entries that reference
events they don't implement (including standard events); however this is
not recommended (and may be forbidden in the future).
Extensions are not required to define menu entries for all events they
implement.
Note: in order to change key bindings, you must currently edit the file
keydefs. It contains two dictionaries named and formatted like the
keydefs dictionaries described above, one for the Unix bindings and one
for the Windows bindings. In the future, a better mechanism will be
provided.

View File

@ -1,157 +0,0 @@
[See end for tips.]
Click on the dotted line at the top of a menu to "tear it off": a
separate window containing the menu is created.
File menu:
New window -- create a new editing window
Open... -- open an existing file
Open module... -- open an existing module (searches sys.path)
Class browser -- show classes and methods in current file
Path browser -- show sys.path directories, modules, classes
and methods
---
Save -- save current window to the associated file (unsaved
windows have a * before and after the window title)
Save As... -- save current window to new file, which becomes
the associated file
Save Copy As... -- save current window to different file
without changing the associated file
---
Close -- close current window (asks to save if unsaved)
Exit -- close all windows and quit IDLE (asks to save if unsaved)
Edit menu:
Undo -- Undo last change to current window (max 1000 changes)
Redo -- Redo last undone change to current window
---
Cut -- Copy selection into system-wide clipboard; then delete selection
Copy -- Copy selection into system-wide clipboard
Paste -- Insert system-wide clipboard into window
Select All -- Select the entire contents of the edit buffer
---
Find... -- Open a search dialog box with many options
Find again -- Repeat last search
Find selection -- Search for the string in the selection
Find in Files... -- Open a search dialog box for searching files
Replace... -- Open a search-and-replace dialog box
Go to line -- Ask for a line number and show that line
---
Indent region -- Shift selected lines right 4 spaces
Dedent region -- Shift selected lines left 4 spaces
Comment out region -- Insert ## in front of selected lines
Uncomment region -- Remove leading # or ## from selected lines
Tabify region -- Turns *leading* stretches of spaces into tabs
Untabify region -- Turn *all* tabs into the right number of spaces
Expand word -- Expand the word you have typed to match another
word in the same buffer; repeat to get a different expansion
Format Paragraph -- Reformat the current blank-line-separated paragraph
---
Import module -- Import or reload the current module
Run script -- Execute the current file in the __main__ namespace
Windows menu:
Zoom Height -- toggles the window between normal size (24x80)
and maximum height.
---
The rest of this menu lists the names of all open windows;
select one to bring it to the foreground (deiconifying it if
necessary).
Debug menu (in the Python Shell window only):
Go to file/line -- look around the insert point for a filename
and linenumber, open the file, and show the line
Open stack viewer -- show the stack traceback of the last exception
Debugger toggle -- Run commands in the shell under the debugger
JIT Stack viewer toggle -- Open stack viewer on traceback
Basic editing and navigation:
Backspace deletes to the left; DEL deletes to the right
Arrow keys and Page Up/Down to move around
Home/End go to begin/end of line
Control-Home/End go to begin/end of file
Some Emacs bindings may also work, e.g. ^B/^P/^A/^E/^D/^L
Automatic indentation:
After a block-opening statement, the next line is indented by
4 spaces (in the Python Shell window by one tab). After
certain keywords (break, return etc.) the next line is
dedented. In leading indentation, Backspace deletes up to 4
spaces if they are there. Tab inserts 1-4 spaces (in the
Python Shell window one tab). See also the indent/dedent
region commands in the edit menu.
Python Shell window:
^C interrupts executing command
^D sends end-of-file; closes window if typed at >>> prompt
Command history:
Alt-p retrieves previous command matching what you have typed
Alt-n retrieves next
Return while on any previous command retrieves that command
Alt-/ (Expand word) is also useful here
Syntax colors:
The coloring is applied in a background "thread", so you may
occasionally see uncolorized text. To change the color
scheme, edit the [Colors] section in config.txt (or add a
[Colors] section to ~/.idle).
Python syntax colors:
Keywords orange
Strings green
Comments red
Definitions blue
Shell colors:
Console output brown
stdout blue
stderr dark green
stdin black
Other preferences:
Most preferences can be changed by editing one of the
configuration text files: config.txt (generic) or one of
config-unix.txt, config-win.txt, config.mac.txt (platform
specific). User-specific preferences can be stored in
$HOME/.idle, which overrides the config*.txt files.
To change keyboard bindings, edit Bindings.py
Command line usage:
idle.py [-c command] [-d] [-e] [-s] [-t title] [arg] ...
-c command run this command
-d enable debugger
-e edit mode; arguments are files to be edited
-s run $IDLESTARTUP or $PYTHONSTARTUP first
-t title set title of shell window
If there are arguments:
If -e is used, arguments are files opened for editing and
sys.argv reflects the arguments passed to IDLE itself.
Otherwise, if -c is used, all arguments are placed in
sys.argv[1:...], with sys.argv[0] set to '-c'.
Otherwise, if neither -e nor -c is used, the first
argument is a script which is executed with the remaining
arguments in sys.argv[1:...] and sys.argv[0] set to the
script name. If the script name is '-', no script is
executed but an interactive Python session is started; the
arguments are still available in sys.argv.

View File

@ -1,12 +0,0 @@
#! /usr/bin/env python
import os
import sys
from idlelib import IdleConf
idle_dir = os.path.dirname(IdleConf.__file__)
IdleConf.load(idle_dir)
# defer importing Pyshell until IdleConf is loaded
from idlelib import PyShell
PyShell.main()

View File

@ -1,3 +0,0 @@
rem idle.bat
start idle.pyw %1 %2 %3 %4 %5 %6 %7 %8 %9

View File

@ -1,4 +0,0 @@
#! /usr/bin/env python
import PyShell
PyShell.main()

View File

@ -1,8 +0,0 @@
try:
import idle
except SystemExit:
raise
except:
import traceback
traceback.print_exc()
raw_input("Hit return to exit...")

View File

@ -1 +0,0 @@
IDLE_VERSION = "0.8"

View File

@ -1,57 +0,0 @@
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>']}

View File

@ -1,100 +0,0 @@
import os, glob, sys
from distutils.core import setup
from distutils.command.build_py import build_py
from distutils.command.install_lib import install_lib
import idlever
try:
pos = sys.argv.index("--check-tkinter")
except ValueError:
pass
else:
del sys.argv[pos]
try:
import _tkinter
except ImportError:
print >>sys.stderr, "Cannot install IDLE without _tkinter"
raise SystemExit
try:
package_dir = os.path.join(os.environ["SRCDIR"], "Tools", "idle")
except KeyError:
package_dir = "."
# name of idle package
idlelib = "idlelib"
# the normal build_py would not incorporate the .txt files
txt_files = ['config-unix.txt','config-win.txt','config.txt', 'help.txt']
Icons = glob.glob1(os.path.join(package_dir,"Icons"),"*.gif")
class idle_build_py(build_py):
def get_plain_outfile(self, build_dir, package, file):
# like get_module_outfile, but does not append .py
outfile_path = [build_dir] + list(package) + [file]
return apply(os.path.join, outfile_path)
def run(self):
# Copies all .py files, then also copies the txt and gif files
build_py.run(self)
assert self.packages == [idlelib]
for name in txt_files:
outfile = self.get_plain_outfile(self.build_lib, [idlelib], name)
dir = os.path.dirname(outfile)
self.mkpath(dir)
self.copy_file(os.path.join(package_dir, name), outfile,
preserve_mode = 0)
for name in Icons:
outfile = self.get_plain_outfile(self.build_lib,
[idlelib,"Icons"], name)
dir = os.path.dirname(outfile)
self.mkpath(dir)
self.copy_file(os.path.join(package_dir, "Icons", name),
outfile, preserve_mode = 0)
def get_source_files(self):
# returns the .py files, the .txt files, and the icons
icons = [os.path.join(package_dir, "Icons",name) for name in Icons]
txts = [os.path.join(package_dir, name) for name in txt_files]
return build_py.get_source_files(self)+txt_files+icons
def get_outputs(self, include_bytecode=1):
# returns the built files
outputs = build_py.get_outputs(self, include_bytecode)
if not include_bytecode:
return outputs
for name in txt_files:
filename = self.get_plain_outfile(self.build_lib,
[idlelib], name)
outputs.append(filename)
for name in Icons:
filename = self.get_plain_outfile(self.build_lib,
[idlelib,"Icons"], name)
outputs.append(filename)
return outputs
# Arghhh. install_lib thinks that all files returned from build_py's
# get_outputs are bytecode files
class idle_install_lib(install_lib):
def _bytecode_filenames(self, files):
files = [n for n in files if n.endswith('.py')]
return install_lib._bytecode_filenames(self,files)
setup(name="IDLE",
version = idlever.IDLE_VERSION,
description = "IDLE, the Python IDE",
author = "Guido van Rossum",
author_email = "guido@python.org",
#url =
long_description =
"""IDLE is a Tkinter based IDE for Python. It is written in 100% pure
Python and works both on Windows and Unix. It features a multi-window
text editor with multiple undo, Python colorizing, and many other things,
as well as a Python shell window and a debugger.""",
cmdclass = {'build_py':idle_build_py,
'install_lib':idle_install_lib},
package_dir = {idlelib: package_dir},
packages = [idlelib],
scripts = [os.path.join(package_dir, 'idle')]
)

View File

@ -1,31 +0,0 @@
import string
def f():
a = 0
b = 1
c = 2
d = 3
e = 4
g()
def g():
h()
def h():
i()
def i():
j()
def j():
k()
def k():
l()
l = lambda: test()
def test():
string.capwords(1)
f()