mirror of
https://github.com/python/cpython.git
synced 2025-11-01 06:01:29 +00:00
Initial revision
This commit is contained in:
parent
33a6da9971
commit
7aced17437
73 changed files with 12383 additions and 0 deletions
92
Lib/idlelib/AutoExpand.py
Normal file
92
Lib/idlelib/AutoExpand.py
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
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>'],
|
||||||
|
}
|
||||||
|
|
||||||
|
menudefs = [
|
||||||
|
('edit', [
|
||||||
|
('E_xpand word', '<<expand-word>>'),
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
|
||||||
|
wordchars = string.letters + string.digits + "_"
|
||||||
|
|
||||||
|
def __init__(self, editwin):
|
||||||
|
self.text = editwin.text
|
||||||
|
self.text.wordlist = None # XXX what is this?
|
||||||
|
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:]
|
||||||
554
Lib/idlelib/AutoIndent.py
Normal file
554
Lib/idlelib/AutoIndent.py
Normal file
|
|
@ -0,0 +1,554 @@
|
||||||
|
import string
|
||||||
|
#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 = [
|
||||||
|
('format', [ # /s/edit/format dscherer@cmu.edu
|
||||||
|
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>>': ['<Shift-Tab>', # dscherer@cmu.edu
|
||||||
|
'<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.
|
||||||
|
expand, tabwidth = string.expandtabs, self.tabwidth
|
||||||
|
have = len(expand(chars, tabwidth))
|
||||||
|
assert have > 0
|
||||||
|
want = int((have - 1) / self.indentwidth) * self.indentwidth
|
||||||
|
ncharsdeleted = 0
|
||||||
|
while 1:
|
||||||
|
chars = chars[:-1]
|
||||||
|
ncharsdeleted = ncharsdeleted + 1
|
||||||
|
have = len(expand(chars, 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(string.expandtabs(prefix,
|
||||||
|
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] = string.expandtabs(lines[pos], 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 = string.split(chars, "\n")
|
||||||
|
return head, tail, chars, lines
|
||||||
|
|
||||||
|
def set_region(self, head, tail, chars, lines):
|
||||||
|
text = self.text
|
||||||
|
newchars = string.join(lines, "\n")
|
||||||
|
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
|
||||||
67
Lib/idlelib/Bindings.py
Normal file
67
Lib/idlelib/Bindings.py
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# changes by dscherer@cmu.edu:
|
||||||
|
# - Python shell moved to 'Run' menu
|
||||||
|
# - "Help" renamed to "IDLE Help" to distinguish from Python help.
|
||||||
|
# The distinction between the environment and the language is dim
|
||||||
|
# or nonexistent in a novice's mind.
|
||||||
|
# - Silly advice added
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import string
|
||||||
|
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>>'),
|
||||||
|
None,
|
||||||
|
('_Save', '<<save-window>>'),
|
||||||
|
('Save _As...', '<<save-window-as-file>>'),
|
||||||
|
('Save Co_py As...', '<<save-copy-of-window-as-file>>'),
|
||||||
|
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>>'),
|
||||||
|
]),
|
||||||
|
('run',[
|
||||||
|
('Python shell', '<<open-python-shell>>'),
|
||||||
|
]),
|
||||||
|
('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', [
|
||||||
|
('_IDLE Help...', '<<help>>'),
|
||||||
|
('Python _Documentation...', '<<python-docs>>'),
|
||||||
|
('_Advice...', '<<good-advice>>'),
|
||||||
|
None,
|
||||||
|
('_About IDLE...', '<<about-idle>>'),
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
default_keydefs = windows_keydefs
|
||||||
|
else:
|
||||||
|
default_keydefs = unix_keydefs
|
||||||
|
|
||||||
|
del sys
|
||||||
71
Lib/idlelib/CallTipWindow.py
Normal file
71
Lib/idlelib/CallTipWindow.py
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
# A CallTip window class for Tkinter/IDLE.
|
||||||
|
# After ToolTip.py, which uses ideas gleaned from PySol
|
||||||
|
|
||||||
|
# Used by the CallTips IDLE extension.
|
||||||
|
import os
|
||||||
|
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):
|
||||||
|
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()
|
||||||
190
Lib/idlelib/CallTips.py
Normal file
190
Lib/idlelib/CallTips.py
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
# CallTips.py - An IDLE extension that provides "Call Tips" - ie, a floating window that
|
||||||
|
# displays parameter information as you open parens.
|
||||||
|
|
||||||
|
import string
|
||||||
|
import sys
|
||||||
|
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.uppercase + string.lowercase + string.digits):
|
||||||
|
# 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 = string.join(items , ", ")
|
||||||
|
argText = "(%s)" % argText
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
# See if we can use the docstring
|
||||||
|
if hasattr(ob, "__doc__") and ob.__doc__:
|
||||||
|
pos = string.find(ob.__doc__, "\n")
|
||||||
|
if pos<0 or pos>70: pos=70
|
||||||
|
if argText: argText = argText + "\n"
|
||||||
|
argText = argText + ob.__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)
|
||||||
1017
Lib/idlelib/ChangeLog
Normal file
1017
Lib/idlelib/ChangeLog
Normal file
File diff suppressed because it is too large
Load diff
224
Lib/idlelib/ClassBrowser.py
Normal file
224
Lib/idlelib/ClassBrowser.py
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
"""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 string
|
||||||
|
import pyclbr
|
||||||
|
|
||||||
|
# XXX Patch pyclbr with dummies if it's vintage Python 1.5.2:
|
||||||
|
if not hasattr(pyclbr, "readmodule_ex"):
|
||||||
|
pyclbr.readmodule_ex = pyclbr.readmodule
|
||||||
|
if not hasattr(pyclbr, "Function"):
|
||||||
|
class Function(pyclbr.Class):
|
||||||
|
pass
|
||||||
|
pyclbr.Function = Function
|
||||||
|
|
||||||
|
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 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)" % string.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):
|
||||||
|
if self.cl:
|
||||||
|
return not not self.cl.methods
|
||||||
|
|
||||||
|
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()
|
||||||
234
Lib/idlelib/ColorDelegator.py
Normal file
234
Lib/idlelib/ColorDelegator.py
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
import time
|
||||||
|
import string
|
||||||
|
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 + string.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)
|
||||||
|
|
||||||
|
class ColorDelegator(Delegator):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
Delegator.__init__(self)
|
||||||
|
self.prog = prog
|
||||||
|
self.idprog = idprog
|
||||||
|
|
||||||
|
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)
|
||||||
|
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()
|
||||||
382
Lib/idlelib/ConfigParser.py
Normal file
382
Lib/idlelib/ConfigParser.py
Normal file
|
|
@ -0,0 +1,382 @@
|
||||||
|
"""Configuration file parser.
|
||||||
|
|
||||||
|
A setup file consists of sections, lead by a "[section]" header,
|
||||||
|
and followed by "name: value" entries, with continuations and such in
|
||||||
|
the style of RFC 822.
|
||||||
|
|
||||||
|
The option values can contain format strings which refer to other values in
|
||||||
|
the same section, or values in a special [DEFAULT] section.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
something: %(dir)s/whatever
|
||||||
|
|
||||||
|
would resolve the "%(dir)s" to the value of dir. All reference
|
||||||
|
expansions are done late, on demand.
|
||||||
|
|
||||||
|
Intrinsic defaults can be specified by passing them into the
|
||||||
|
ConfigParser constructor as a dictionary.
|
||||||
|
|
||||||
|
class:
|
||||||
|
|
||||||
|
ConfigParser -- responsible for for parsing a list of
|
||||||
|
configuration files, and managing the parsed database.
|
||||||
|
|
||||||
|
methods:
|
||||||
|
|
||||||
|
__init__(defaults=None)
|
||||||
|
create the parser and specify a dictionary of intrinsic defaults. The
|
||||||
|
keys must be strings, the values must be appropriate for %()s string
|
||||||
|
interpolation. Note that `__name__' is always an intrinsic default;
|
||||||
|
it's value is the section's name.
|
||||||
|
|
||||||
|
sections()
|
||||||
|
return all the configuration section names, sans DEFAULT
|
||||||
|
|
||||||
|
has_section(section)
|
||||||
|
return whether the given section exists
|
||||||
|
|
||||||
|
options(section)
|
||||||
|
return list of configuration options for the named section
|
||||||
|
|
||||||
|
has_option(section, option)
|
||||||
|
return whether the given section has the given option
|
||||||
|
|
||||||
|
read(filenames)
|
||||||
|
read and parse the list of named configuration files, given by
|
||||||
|
name. A single filename is also allowed. Non-existing files
|
||||||
|
are ignored.
|
||||||
|
|
||||||
|
readfp(fp, filename=None)
|
||||||
|
read and parse one configuration file, given as a file object.
|
||||||
|
The filename defaults to fp.name; it is only used in error
|
||||||
|
messages (if fp has no `name' attribute, the string `<???>' is used).
|
||||||
|
|
||||||
|
get(section, option, raw=0, vars=None)
|
||||||
|
return a string value for the named option. All % interpolations are
|
||||||
|
expanded in the return values, based on the defaults passed into the
|
||||||
|
constructor and the DEFAULT section. Additional substitutions may be
|
||||||
|
provided using the `vars' argument, which must be a dictionary whose
|
||||||
|
contents override any pre-existing defaults.
|
||||||
|
|
||||||
|
getint(section, options)
|
||||||
|
like get(), but convert value to an integer
|
||||||
|
|
||||||
|
getfloat(section, options)
|
||||||
|
like get(), but convert value to a float
|
||||||
|
|
||||||
|
getboolean(section, options)
|
||||||
|
like get(), but convert value to a boolean (currently defined as 0 or
|
||||||
|
1, only)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import string
|
||||||
|
import re
|
||||||
|
|
||||||
|
DEFAULTSECT = "DEFAULT"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# exception classes
|
||||||
|
class Error:
|
||||||
|
def __init__(self, msg=''):
|
||||||
|
self._msg = msg
|
||||||
|
def __repr__(self):
|
||||||
|
return self._msg
|
||||||
|
|
||||||
|
class NoSectionError(Error):
|
||||||
|
def __init__(self, section):
|
||||||
|
Error.__init__(self, 'No section: %s' % section)
|
||||||
|
self.section = section
|
||||||
|
|
||||||
|
class DuplicateSectionError(Error):
|
||||||
|
def __init__(self, section):
|
||||||
|
Error.__init__(self, "Section %s already exists" % section)
|
||||||
|
self.section = section
|
||||||
|
|
||||||
|
class NoOptionError(Error):
|
||||||
|
def __init__(self, option, section):
|
||||||
|
Error.__init__(self, "No option `%s' in section: %s" %
|
||||||
|
(option, section))
|
||||||
|
self.option = option
|
||||||
|
self.section = section
|
||||||
|
|
||||||
|
class InterpolationError(Error):
|
||||||
|
def __init__(self, reference, option, section, rawval):
|
||||||
|
Error.__init__(self,
|
||||||
|
"Bad value substitution:\n"
|
||||||
|
"\tsection: [%s]\n"
|
||||||
|
"\toption : %s\n"
|
||||||
|
"\tkey : %s\n"
|
||||||
|
"\trawval : %s\n"
|
||||||
|
% (section, option, reference, rawval))
|
||||||
|
self.reference = reference
|
||||||
|
self.option = option
|
||||||
|
self.section = section
|
||||||
|
|
||||||
|
class MissingSectionHeaderError(Error):
|
||||||
|
def __init__(self, filename, lineno, line):
|
||||||
|
Error.__init__(
|
||||||
|
self,
|
||||||
|
'File contains no section headers.\nfile: %s, line: %d\n%s' %
|
||||||
|
(filename, lineno, line))
|
||||||
|
self.filename = filename
|
||||||
|
self.lineno = lineno
|
||||||
|
self.line = line
|
||||||
|
|
||||||
|
class ParsingError(Error):
|
||||||
|
def __init__(self, filename):
|
||||||
|
Error.__init__(self, 'File contains parsing errors: %s' % filename)
|
||||||
|
self.filename = filename
|
||||||
|
self.errors = []
|
||||||
|
|
||||||
|
def append(self, lineno, line):
|
||||||
|
self.errors.append((lineno, line))
|
||||||
|
self._msg = self._msg + '\n\t[line %2d]: %s' % (lineno, line)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigParser:
|
||||||
|
def __init__(self, defaults=None):
|
||||||
|
self.__sections = {}
|
||||||
|
if defaults is None:
|
||||||
|
self.__defaults = {}
|
||||||
|
else:
|
||||||
|
self.__defaults = defaults
|
||||||
|
|
||||||
|
def defaults(self):
|
||||||
|
return self.__defaults
|
||||||
|
|
||||||
|
def sections(self):
|
||||||
|
"""Return a list of section names, excluding [DEFAULT]"""
|
||||||
|
# self.__sections will never have [DEFAULT] in it
|
||||||
|
return self.__sections.keys()
|
||||||
|
|
||||||
|
def add_section(self, section):
|
||||||
|
"""Create a new section in the configuration.
|
||||||
|
|
||||||
|
Raise DuplicateSectionError if a section by the specified name
|
||||||
|
already exists.
|
||||||
|
"""
|
||||||
|
if self.__sections.has_key(section):
|
||||||
|
raise DuplicateSectionError(section)
|
||||||
|
self.__sections[section] = {}
|
||||||
|
|
||||||
|
def has_section(self, section):
|
||||||
|
"""Indicate whether the named section is present in the configuration.
|
||||||
|
|
||||||
|
The DEFAULT section is not acknowledged.
|
||||||
|
"""
|
||||||
|
return self.__sections.has_key(section)
|
||||||
|
|
||||||
|
def options(self, section):
|
||||||
|
"""Return a list of option names for the given section name."""
|
||||||
|
try:
|
||||||
|
opts = self.__sections[section].copy()
|
||||||
|
except KeyError:
|
||||||
|
raise NoSectionError(section)
|
||||||
|
opts.update(self.__defaults)
|
||||||
|
return opts.keys()
|
||||||
|
|
||||||
|
def has_option(self, section, option):
|
||||||
|
"""Return whether the given section has the given option."""
|
||||||
|
try:
|
||||||
|
opts = self.__sections[section]
|
||||||
|
except KeyError:
|
||||||
|
raise NoSectionError(section)
|
||||||
|
return opts.has_key(option)
|
||||||
|
|
||||||
|
def read(self, filenames):
|
||||||
|
"""Read and parse a filename or a list of filenames.
|
||||||
|
|
||||||
|
Files that cannot be opened are silently ignored; this is
|
||||||
|
designed so that you can specify a list of potential
|
||||||
|
configuration file locations (e.g. current directory, user's
|
||||||
|
home directory, systemwide directory), and all existing
|
||||||
|
configuration files in the list will be read. A single
|
||||||
|
filename may also be given.
|
||||||
|
"""
|
||||||
|
if type(filenames) is type(''):
|
||||||
|
filenames = [filenames]
|
||||||
|
for filename in filenames:
|
||||||
|
try:
|
||||||
|
fp = open(filename)
|
||||||
|
except IOError:
|
||||||
|
continue
|
||||||
|
self.__read(fp, filename)
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
def readfp(self, fp, filename=None):
|
||||||
|
"""Like read() but the argument must be a file-like object.
|
||||||
|
|
||||||
|
The `fp' argument must have a `readline' method. Optional
|
||||||
|
second argument is the `filename', which if not given, is
|
||||||
|
taken from fp.name. If fp has no `name' attribute, `<???>' is
|
||||||
|
used.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if filename is None:
|
||||||
|
try:
|
||||||
|
filename = fp.name
|
||||||
|
except AttributeError:
|
||||||
|
filename = '<???>'
|
||||||
|
self.__read(fp, filename)
|
||||||
|
|
||||||
|
def get(self, section, option, raw=0, vars=None):
|
||||||
|
"""Get an option value for a given section.
|
||||||
|
|
||||||
|
All % interpolations are expanded in the return values, based on the
|
||||||
|
defaults passed into the constructor, unless the optional argument
|
||||||
|
`raw' is true. Additional substitutions may be provided using the
|
||||||
|
`vars' argument, which must be a dictionary whose contents overrides
|
||||||
|
any pre-existing defaults.
|
||||||
|
|
||||||
|
The section DEFAULT is special.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
sectdict = self.__sections[section].copy()
|
||||||
|
except KeyError:
|
||||||
|
if section == DEFAULTSECT:
|
||||||
|
sectdict = {}
|
||||||
|
else:
|
||||||
|
raise NoSectionError(section)
|
||||||
|
d = self.__defaults.copy()
|
||||||
|
d.update(sectdict)
|
||||||
|
# Update with the entry specific variables
|
||||||
|
if vars:
|
||||||
|
d.update(vars)
|
||||||
|
option = self.optionxform(option)
|
||||||
|
try:
|
||||||
|
rawval = d[option]
|
||||||
|
except KeyError:
|
||||||
|
raise NoOptionError(option, section)
|
||||||
|
# do the string interpolation
|
||||||
|
if raw:
|
||||||
|
return rawval
|
||||||
|
|
||||||
|
value = rawval # Make it a pretty variable name
|
||||||
|
depth = 0
|
||||||
|
while depth < 10: # Loop through this until it's done
|
||||||
|
depth = depth + 1
|
||||||
|
if string.find(value, "%(") >= 0:
|
||||||
|
try:
|
||||||
|
value = value % d
|
||||||
|
except KeyError, key:
|
||||||
|
raise InterpolationError(key, option, section, rawval)
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __get(self, section, conv, option):
|
||||||
|
return conv(self.get(section, option))
|
||||||
|
|
||||||
|
def getint(self, section, option):
|
||||||
|
return self.__get(section, string.atoi, option)
|
||||||
|
|
||||||
|
def getfloat(self, section, option):
|
||||||
|
return self.__get(section, string.atof, option)
|
||||||
|
|
||||||
|
def getboolean(self, section, option):
|
||||||
|
v = self.get(section, option)
|
||||||
|
val = string.atoi(v)
|
||||||
|
if val not in (0, 1):
|
||||||
|
raise ValueError, 'Not a boolean: %s' % v
|
||||||
|
return val
|
||||||
|
|
||||||
|
def optionxform(self, optionstr):
|
||||||
|
return string.lower(optionstr)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Regular expressions for parsing section headers and options. Note a
|
||||||
|
# slight semantic change from the previous version, because of the use
|
||||||
|
# of \w, _ is allowed in section header names.
|
||||||
|
SECTCRE = re.compile(
|
||||||
|
r'\[' # [
|
||||||
|
r'(?P<header>[-\w_.*,(){}]+)' # a lot of stuff found by IvL
|
||||||
|
r'\]' # ]
|
||||||
|
)
|
||||||
|
OPTCRE = re.compile(
|
||||||
|
r'(?P<option>[-\w_.*,(){}]+)' # a lot of stuff found by IvL
|
||||||
|
r'[ \t]*(?P<vi>[:=])[ \t]*' # any number of space/tab,
|
||||||
|
# followed by separator
|
||||||
|
# (either : or =), followed
|
||||||
|
# by any # space/tab
|
||||||
|
r'(?P<value>.*)$' # everything up to eol
|
||||||
|
)
|
||||||
|
|
||||||
|
def __read(self, fp, fpname):
|
||||||
|
"""Parse a sectioned setup file.
|
||||||
|
|
||||||
|
The sections in setup file contains a title line at the top,
|
||||||
|
indicated by a name in square brackets (`[]'), plus key/value
|
||||||
|
options lines, indicated by `name: value' format lines.
|
||||||
|
Continuation are represented by an embedded newline then
|
||||||
|
leading whitespace. Blank lines, lines beginning with a '#',
|
||||||
|
and just about everything else is ignored.
|
||||||
|
"""
|
||||||
|
cursect = None # None, or a dictionary
|
||||||
|
optname = None
|
||||||
|
lineno = 0
|
||||||
|
e = None # None, or an exception
|
||||||
|
while 1:
|
||||||
|
line = fp.readline()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
lineno = lineno + 1
|
||||||
|
# comment or blank line?
|
||||||
|
if string.strip(line) == '' or line[0] in '#;':
|
||||||
|
continue
|
||||||
|
if string.lower(string.split(line)[0]) == 'rem' \
|
||||||
|
and line[0] in "rR": # no leading whitespace
|
||||||
|
continue
|
||||||
|
# continuation line?
|
||||||
|
if line[0] in ' \t' and cursect is not None and optname:
|
||||||
|
value = string.strip(line)
|
||||||
|
if value:
|
||||||
|
cursect[optname] = cursect[optname] + '\n ' + value
|
||||||
|
# a section header or option header?
|
||||||
|
else:
|
||||||
|
# is it a section header?
|
||||||
|
mo = self.SECTCRE.match(line)
|
||||||
|
if mo:
|
||||||
|
sectname = mo.group('header')
|
||||||
|
if self.__sections.has_key(sectname):
|
||||||
|
cursect = self.__sections[sectname]
|
||||||
|
elif sectname == DEFAULTSECT:
|
||||||
|
cursect = self.__defaults
|
||||||
|
else:
|
||||||
|
cursect = {'__name__': sectname}
|
||||||
|
self.__sections[sectname] = cursect
|
||||||
|
# So sections can't start with a continuation line
|
||||||
|
optname = None
|
||||||
|
# no section header in the file?
|
||||||
|
elif cursect is None:
|
||||||
|
raise MissingSectionHeaderError(fpname, lineno, `line`)
|
||||||
|
# an option line?
|
||||||
|
else:
|
||||||
|
mo = self.OPTCRE.match(line)
|
||||||
|
if mo:
|
||||||
|
optname, vi, optval = mo.group('option', 'vi', 'value')
|
||||||
|
optname = string.lower(optname)
|
||||||
|
if vi in ('=', ':') and ';' in optval:
|
||||||
|
# ';' is a comment delimiter only if it follows
|
||||||
|
# a spacing character
|
||||||
|
pos = string.find(optval, ';')
|
||||||
|
if pos and optval[pos-1] in string.whitespace:
|
||||||
|
optval = optval[:pos]
|
||||||
|
optval = string.strip(optval)
|
||||||
|
# allow empty values
|
||||||
|
if optval == '""':
|
||||||
|
optval = ''
|
||||||
|
cursect[optname] = optval
|
||||||
|
else:
|
||||||
|
# a non-fatal parsing error occurred. set up the
|
||||||
|
# exception but keep going. the exception will be
|
||||||
|
# raised at the end of the file and will contain a
|
||||||
|
# list of all bogus lines
|
||||||
|
if not e:
|
||||||
|
e = ParsingError(fpname)
|
||||||
|
e.append(lineno, `line`)
|
||||||
|
# if any parsing errors occurred, raise an exception
|
||||||
|
if e:
|
||||||
|
raise e
|
||||||
308
Lib/idlelib/Debugger.py
Normal file
308
Lib/idlelib/Debugger.py
Normal file
|
|
@ -0,0 +1,308 @@
|
||||||
|
import os
|
||||||
|
import bdb
|
||||||
|
import traceback
|
||||||
|
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
|
||||||
|
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)
|
||||||
34
Lib/idlelib/Delegator.py
Normal file
34
Lib/idlelib/Delegator.py
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
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
|
||||||
749
Lib/idlelib/EditorWindow.py
Normal file
749
Lib/idlelib/EditorWindow.py
Normal file
|
|
@ -0,0 +1,749 @@
|
||||||
|
# changes by dscherer@cmu.edu
|
||||||
|
# - created format and run menus
|
||||||
|
# - added silly advice dialog (apologies to Douglas Adams)
|
||||||
|
# - made Python Documentation work on Windows (requires win32api to
|
||||||
|
# do a ShellExecute(); other ways of starting a web browser are awkward)
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import string
|
||||||
|
import re
|
||||||
|
import imp
|
||||||
|
from Tkinter import *
|
||||||
|
import tkSimpleDialog
|
||||||
|
import tkMessageBox
|
||||||
|
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
|
||||||
|
|
||||||
|
This version of IDLE has been modified by David Scherer
|
||||||
|
(dscherer@cmu.edu). See readme.txt for details.
|
||||||
|
""" % idlever.IDLE_VERSION
|
||||||
|
|
||||||
|
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 = {}
|
||||||
|
|
||||||
|
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("<<good-advice>>", self.good_advice)
|
||||||
|
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 = string.split(self.text.index(INSERT), '.')
|
||||||
|
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"),
|
||||||
|
("format", "F_ormat"),
|
||||||
|
("run", "_Run"),
|
||||||
|
("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 good_advice(self, event=None):
|
||||||
|
tkMessageBox.showinfo('Advice', "Don't Panic!", master=self.text)
|
||||||
|
|
||||||
|
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_viewer = "netscape -remote 'openurl(%(url)s)' 2>/dev/null || " \
|
||||||
|
"netscape %(url)s &"
|
||||||
|
help_url = "http://www.python.org/doc/current/"
|
||||||
|
|
||||||
|
def python_docs(self, event=None):
|
||||||
|
if sys.platform=='win32':
|
||||||
|
try:
|
||||||
|
import win32api
|
||||||
|
import ExecBinding
|
||||||
|
doc = os.path.join( os.path.dirname( ExecBinding.pyth_exe ), "doc", "index.html" )
|
||||||
|
win32api.ShellExecute(0, None, doc, None, sys.path[0], 1)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
cmd = self.help_viewer % {"url": self.help_url}
|
||||||
|
os.system(cmd)
|
||||||
|
|
||||||
|
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 = string.strip(name)
|
||||||
|
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 = string.strip(name)
|
||||||
|
if not name:
|
||||||
|
return
|
||||||
|
# XXX Ought to support package syntax
|
||||||
|
# XXX Ought to insert current file's directory in front of path
|
||||||
|
try:
|
||||||
|
(f, file, (suffix, mode, type)) = imp.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 1
|
||||||
|
base, ext = os.path.splitext(os.path.basename(filename))
|
||||||
|
if os.path.normcase(ext) in (".py", ".pyw"):
|
||||||
|
return 1
|
||||||
|
try:
|
||||||
|
f = open(filename)
|
||||||
|
line = f.readline()
|
||||||
|
f.close()
|
||||||
|
except IOError:
|
||||||
|
return 0
|
||||||
|
return line[:2] == '#!' and string.find(line, 'python') >= 0
|
||||||
|
|
||||||
|
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 = string.replace(vevent, "-", "_")
|
||||||
|
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" * newtabwith)
|
||||||
|
text.configure(tabs=pixels)
|
||||||
|
|
||||||
|
def prepstr(s):
|
||||||
|
# Helper to extract the underscore from a string, e.g.
|
||||||
|
# prepstr("Co_py") returns (2, "Copy").
|
||||||
|
i = string.find(s, '_')
|
||||||
|
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: string.upper(m.group()), s)
|
||||||
|
s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
|
||||||
|
s = re.sub("Key-", "", s)
|
||||||
|
s = re.sub("Cancel","Ctrl-Break",s) # dscherer@cmu.edu
|
||||||
|
s = re.sub("Control-", "Ctrl-", s)
|
||||||
|
s = re.sub("-", "+", s)
|
||||||
|
s = re.sub("><", " ", s)
|
||||||
|
s = re.sub("<", "", s)
|
||||||
|
s = re.sub(">", "", s)
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def fixwordbreaks(root):
|
||||||
|
# Make sure that Tk's double-click and next/previous word
|
||||||
|
# operations use our definition of a word (i.e. an identifier)
|
||||||
|
tk = root.tk
|
||||||
|
tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
|
||||||
|
tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
|
||||||
|
tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
|
||||||
|
|
||||||
|
|
||||||
|
def test():
|
||||||
|
root = Tk()
|
||||||
|
fixwordbreaks(root)
|
||||||
|
root.withdraw()
|
||||||
|
if sys.argv[1:]:
|
||||||
|
filename = sys.argv[1]
|
||||||
|
else:
|
||||||
|
filename = None
|
||||||
|
edit = EditorWindow(root=root, filename=filename)
|
||||||
|
edit.set_close_hook(root.quit)
|
||||||
|
root.mainloop()
|
||||||
|
root.destroy()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test()
|
||||||
198
Lib/idlelib/ExecBinding.py
Normal file
198
Lib/idlelib/ExecBinding.py
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
"""Extension to execute a script in a separate process
|
||||||
|
|
||||||
|
David Scherer <dscherer@cmu.edu>
|
||||||
|
|
||||||
|
The ExecBinding module, a replacement for ScriptBinding, executes
|
||||||
|
programs in a separate process. Unlike previous versions, this version
|
||||||
|
communicates with the user process via an RPC protocol (see the 'protocol'
|
||||||
|
module). The user program is loaded by the 'loader' and 'Remote'
|
||||||
|
modules. Its standard output and input are directed back to the
|
||||||
|
ExecBinding class through the RPC mechanism and implemented here.
|
||||||
|
|
||||||
|
A "stop program" command is provided and bound to control-break. Closing
|
||||||
|
the output window also stops the running program.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import imp
|
||||||
|
import OutputWindow
|
||||||
|
import protocol
|
||||||
|
import spawn
|
||||||
|
import traceback
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
# Find Python and the loader. This should be done as early in execution
|
||||||
|
# as possible, because if the current directory or sys.path is changed
|
||||||
|
# it may no longer be possible to get correct paths for these things.
|
||||||
|
|
||||||
|
pyth_exe = spawn.hardpath( sys.executable )
|
||||||
|
load_py = spawn.hardpath( imp.find_module("loader")[1] )
|
||||||
|
|
||||||
|
# The following mechanism matches loaders up with ExecBindings that are
|
||||||
|
# trying to load something.
|
||||||
|
|
||||||
|
waiting_for_loader = []
|
||||||
|
|
||||||
|
def loader_connect(client, addr):
|
||||||
|
if waiting_for_loader:
|
||||||
|
a = waiting_for_loader.pop(0)
|
||||||
|
try:
|
||||||
|
return a.connect(client, addr)
|
||||||
|
except:
|
||||||
|
return loader_connect(client,addr)
|
||||||
|
|
||||||
|
protocol.publish('ExecBinding', loader_connect)
|
||||||
|
|
||||||
|
class ExecBinding:
|
||||||
|
keydefs = {
|
||||||
|
'<<run-complete-script>>': ['<F5>'],
|
||||||
|
'<<stop-execution>>': ['<Cancel>'], #'<Control-c>'
|
||||||
|
}
|
||||||
|
|
||||||
|
menudefs = [
|
||||||
|
('run', [None,
|
||||||
|
('Run program', '<<run-complete-script>>'),
|
||||||
|
('Stop program', '<<stop-execution>>'),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
delegate = 1
|
||||||
|
|
||||||
|
def __init__(self, editwin):
|
||||||
|
self.editwin = editwin
|
||||||
|
self.client = None
|
||||||
|
self.temp = []
|
||||||
|
|
||||||
|
if not hasattr(editwin, 'source_window'):
|
||||||
|
self.delegate = 0
|
||||||
|
self.output = OutputWindow.OnDemandOutputWindow(editwin.flist)
|
||||||
|
self.output.close_hook = self.stopProgram
|
||||||
|
self.output.source_window = editwin
|
||||||
|
else:
|
||||||
|
if (self.editwin.source_window and
|
||||||
|
self.editwin.source_window.extensions.has_key('ExecBinding') and
|
||||||
|
not self.editwin.source_window.extensions['ExecBinding'].delegate):
|
||||||
|
delegate = self.editwin.source_window.extensions['ExecBinding']
|
||||||
|
self.run_complete_script_event = delegate.run_complete_script_event
|
||||||
|
self.stop_execution_event = delegate.stop_execution_event
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.stopProgram()
|
||||||
|
|
||||||
|
def stop_execution_event(self, event):
|
||||||
|
if self.client:
|
||||||
|
self.stopProgram()
|
||||||
|
self.write('\nProgram stopped.\n','stderr')
|
||||||
|
|
||||||
|
def run_complete_script_event(self, event):
|
||||||
|
filename = self.getfilename()
|
||||||
|
if not filename: return
|
||||||
|
filename = os.path.abspath(filename)
|
||||||
|
|
||||||
|
self.stopProgram()
|
||||||
|
|
||||||
|
self.commands = [ ('run', filename) ]
|
||||||
|
waiting_for_loader.append(self)
|
||||||
|
spawn.spawn( pyth_exe, load_py )
|
||||||
|
|
||||||
|
def connect(self, client, addr):
|
||||||
|
# Called by loader_connect() above. It is remotely possible that
|
||||||
|
# we get connected to two loaders if the user is running the
|
||||||
|
# program repeatedly in a short span of time. In this case, we
|
||||||
|
# simply return None, refusing to connect and letting the redundant
|
||||||
|
# loader die.
|
||||||
|
if self.client: return None
|
||||||
|
|
||||||
|
self.client = client
|
||||||
|
client.set_close_hook( self.connect_lost )
|
||||||
|
|
||||||
|
title = self.editwin.short_title()
|
||||||
|
if title:
|
||||||
|
self.output.set_title(title + " Output")
|
||||||
|
else:
|
||||||
|
self.output.set_title("Output")
|
||||||
|
self.output.write('\n',"stderr")
|
||||||
|
self.output.scroll_clear()
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def connect_lost(self):
|
||||||
|
# Called by the client's close hook when the loader closes its
|
||||||
|
# socket.
|
||||||
|
|
||||||
|
# We print a disconnect message only if the output window is already
|
||||||
|
# open.
|
||||||
|
if self.output.owin and self.output.owin.text:
|
||||||
|
self.output.owin.interrupt()
|
||||||
|
self.output.write("\nProgram disconnected.\n","stderr")
|
||||||
|
|
||||||
|
for t in self.temp:
|
||||||
|
try:
|
||||||
|
os.remove(t)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.temp = []
|
||||||
|
self.client = None
|
||||||
|
|
||||||
|
def get_command(self):
|
||||||
|
# Called by Remote to find out what it should be executing.
|
||||||
|
# Later this will be used to implement debugging, interactivity, etc.
|
||||||
|
if self.commands:
|
||||||
|
return self.commands.pop(0)
|
||||||
|
return ('finish',)
|
||||||
|
|
||||||
|
def program_exception(self, type, value, tb, first, last):
|
||||||
|
if type == SystemExit: return 0
|
||||||
|
|
||||||
|
for i in range(len(tb)):
|
||||||
|
filename, lineno, name, line = tb[i]
|
||||||
|
if filename in self.temp:
|
||||||
|
filename = 'Untitled'
|
||||||
|
tb[i] = filename, lineno, name, line
|
||||||
|
|
||||||
|
list = traceback.format_list(tb[first:last])
|
||||||
|
exc = traceback.format_exception_only( type, value )
|
||||||
|
|
||||||
|
self.write('Traceback (innermost last)\n', 'stderr')
|
||||||
|
for i in (list+exc):
|
||||||
|
self.write(i, 'stderr')
|
||||||
|
|
||||||
|
self.commands = []
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def write(self, text, tag):
|
||||||
|
self.output.write(text,tag)
|
||||||
|
|
||||||
|
def readline(self):
|
||||||
|
return self.output.readline()
|
||||||
|
|
||||||
|
def stopProgram(self):
|
||||||
|
if self.client:
|
||||||
|
self.client.close()
|
||||||
|
self.client = None
|
||||||
|
|
||||||
|
def getfilename(self):
|
||||||
|
# Save all files which have been named, because they might be modules
|
||||||
|
for edit in self.editwin.flist.inversedict.keys():
|
||||||
|
if edit.io and edit.io.filename and not edit.get_saved():
|
||||||
|
edit.io.save(None)
|
||||||
|
|
||||||
|
# Experimental: execute unnamed buffer
|
||||||
|
if not self.editwin.io.filename:
|
||||||
|
filename = os.path.normcase(os.path.abspath(tempfile.mktemp()))
|
||||||
|
self.temp.append(filename)
|
||||||
|
if self.editwin.io.writefile(filename):
|
||||||
|
return filename
|
||||||
|
|
||||||
|
# If the file isn't save, we save it. If it doesn't have a filename,
|
||||||
|
# the user will be prompted.
|
||||||
|
if self.editwin.io and not self.editwin.get_saved():
|
||||||
|
self.editwin.io.save(None)
|
||||||
|
|
||||||
|
# If the file *still* isn't saved, we give up.
|
||||||
|
if not self.editwin.get_saved():
|
||||||
|
return
|
||||||
|
|
||||||
|
return self.editwin.io.filename
|
||||||
150
Lib/idlelib/FileList.py
Normal file
150
Lib/idlelib/FileList.py
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
# changes by dscherer@cmu.edu
|
||||||
|
# - FileList.open() takes an optional 3rd parameter action, which is
|
||||||
|
# called instead of creating a new EditorWindow. This enables
|
||||||
|
# things like 'open in same window'.
|
||||||
|
|
||||||
|
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 goodname(self, filename):
|
||||||
|
filename = self.canonize(filename)
|
||||||
|
key = os.path.normcase(filename)
|
||||||
|
if self.dict.has_key(key):
|
||||||
|
edit = self.dict[key]
|
||||||
|
filename = edit.io.filename or filename
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def open(self, filename, action=None):
|
||||||
|
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)
|
||||||
|
if action is None:
|
||||||
|
return self.EditorWindow(self, filename, key)
|
||||||
|
else:
|
||||||
|
return action(filename)
|
||||||
|
|
||||||
|
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()
|
||||||
155
Lib/idlelib/FormatParagraph.py
Normal file
155
Lib/idlelib/FormatParagraph.py
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
# 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 string
|
||||||
|
import re
|
||||||
|
|
||||||
|
class FormatParagraph:
|
||||||
|
|
||||||
|
menudefs = [
|
||||||
|
('format', [ # /s/edit/format dscherer@cmu.edu
|
||||||
|
('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 = string.split(data, "\n")
|
||||||
|
lines = map(lambda st, l=len(comment_header): st[l:], lines)
|
||||||
|
data = string.join(lines, "\n")
|
||||||
|
# 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 = string.split(newdata, "\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 = string.join(map(builder, newdata), '\n') + 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, string.split(mark, "."))
|
||||||
|
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 = string.split(data, "\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(string.expandtabs(partial + word)) > limit and \
|
||||||
|
partial != indent1:
|
||||||
|
new.append(string.rstrip(partial))
|
||||||
|
partial = indent2
|
||||||
|
partial = partial + word + " "
|
||||||
|
if j+1 < len(words) and words[j+1] != " ":
|
||||||
|
partial = partial + " "
|
||||||
|
i = i+1
|
||||||
|
new.append(string.rstrip(partial))
|
||||||
|
# XXX Should reformat remaining paragraphs as well
|
||||||
|
new.extend(lines[i:])
|
||||||
|
return string.join(new, "\n")
|
||||||
|
|
||||||
|
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)
|
||||||
38
Lib/idlelib/FrameViewer.py
Normal file
38
Lib/idlelib/FrameViewer.py
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
from repr import Repr
|
||||||
|
from Tkinter import *
|
||||||
|
|
||||||
|
class FrameViewer:
|
||||||
|
|
||||||
|
def __init__(self, root, frame):
|
||||||
|
self.root = root
|
||||||
|
self.frame = frame
|
||||||
|
self.top = Toplevel(self.root)
|
||||||
|
self.repr = Repr()
|
||||||
|
self.repr.maxstring = 60
|
||||||
|
self.load_variables()
|
||||||
|
|
||||||
|
def load_variables(self):
|
||||||
|
row = 0
|
||||||
|
if self.frame.f_locals is not self.frame.f_globals:
|
||||||
|
l = Label(self.top, text="Local Variables",
|
||||||
|
borderwidth=2, relief="raised")
|
||||||
|
l.grid(row=row, column=0, columnspan=2, sticky="ew")
|
||||||
|
row = self.load_names(self.frame.f_locals, row+1)
|
||||||
|
l = Label(self.top, text="Global Variables",
|
||||||
|
borderwidth=2, relief="raised")
|
||||||
|
l.grid(row=row, column=0, columnspan=2, sticky="ew")
|
||||||
|
row = self.load_names(self.frame.f_globals, row+1)
|
||||||
|
|
||||||
|
def load_names(self, dict, row):
|
||||||
|
names = dict.keys()
|
||||||
|
names.sort()
|
||||||
|
for name in names:
|
||||||
|
value = dict[name]
|
||||||
|
svalue = self.repr.repr(value)
|
||||||
|
l = Label(self.top, text=name)
|
||||||
|
l.grid(row=row, column=0, sticky="w")
|
||||||
|
l = Entry(self.top, width=60, borderwidth=0)
|
||||||
|
l.insert(0, svalue)
|
||||||
|
l.grid(row=row, column=1, sticky="w")
|
||||||
|
row = row+1
|
||||||
|
return row
|
||||||
135
Lib/idlelib/GrepDialog.py
Normal file
135
Lib/idlelib/GrepDialog.py
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
import string
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import fnmatch
|
||||||
|
import sys
|
||||||
|
from Tkinter import *
|
||||||
|
import tkMessageBox
|
||||||
|
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()
|
||||||
254
Lib/idlelib/IOBinding.py
Normal file
254
Lib/idlelib/IOBinding.py
Normal file
|
|
@ -0,0 +1,254 @@
|
||||||
|
# changes by dscherer@cmu.edu
|
||||||
|
# - IOBinding.open() replaces the current window with the opened file,
|
||||||
|
# if the current window is both unmodified and unnamed
|
||||||
|
# - IOBinding.loadfile() interprets Windows, UNIX, and Macintosh
|
||||||
|
# end-of-line conventions, instead of relying on the standard library,
|
||||||
|
# which will only understand the local convention.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import tkFileDialog
|
||||||
|
import tkMessageBox
|
||||||
|
import re
|
||||||
|
|
||||||
|
#$ 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>
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
# 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:
|
||||||
|
# if the current window has no filename and hasn't been
|
||||||
|
# modified, we replace it's contents (no loss). Otherwise
|
||||||
|
# we open a new window.
|
||||||
|
if not self.filename and self.get_saved():
|
||||||
|
self.editwin.flist.open(filename, self.loadfile)
|
||||||
|
else:
|
||||||
|
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:
|
||||||
|
# open the file in binary mode so that we can handle
|
||||||
|
# end-of-line convention ourselves.
|
||||||
|
f = open(filename,'rb')
|
||||||
|
chars = f.read()
|
||||||
|
f.close()
|
||||||
|
except IOError, msg:
|
||||||
|
tkMessageBox.showerror("I/O Error", str(msg), master=self.text)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# We now convert all end-of-lines to '\n's
|
||||||
|
eol = r"(\r\n)|\n|\r" # \r\n (Windows), \n (UNIX), or \r (Mac)
|
||||||
|
chars = re.compile( eol ).sub( r"\n", 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 1
|
||||||
|
|
||||||
|
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 writefile(self, filename):
|
||||||
|
self.fixlastline()
|
||||||
|
try:
|
||||||
|
f = open(filename, "w")
|
||||||
|
chars = self.text.get("1.0", "end-1c")
|
||||||
|
f.write(chars)
|
||||||
|
f.close()
|
||||||
|
## print "saved to", `filename`
|
||||||
|
return 1
|
||||||
|
except IOError, msg:
|
||||||
|
tkMessageBox.showerror("I/O Error", str(msg),
|
||||||
|
master=self.text)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
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():
|
||||||
|
from Tkinter import *
|
||||||
|
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__":
|
||||||
|
test()
|
||||||
BIN
Lib/idlelib/Icons/folder.gif
Normal file
BIN
Lib/idlelib/Icons/folder.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 120 B |
BIN
Lib/idlelib/Icons/minusnode.gif
Normal file
BIN
Lib/idlelib/Icons/minusnode.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 75 B |
BIN
Lib/idlelib/Icons/openfolder.gif
Normal file
BIN
Lib/idlelib/Icons/openfolder.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 125 B |
BIN
Lib/idlelib/Icons/plusnode.gif
Normal file
BIN
Lib/idlelib/Icons/plusnode.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 B |
BIN
Lib/idlelib/Icons/python.gif
Normal file
BIN
Lib/idlelib/Icons/python.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 895 B |
BIN
Lib/idlelib/Icons/tk.gif
Normal file
BIN
Lib/idlelib/Icons/tk.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 85 B |
113
Lib/idlelib/IdleConf.py
Normal file
113
Lib/idlelib/IdleConf.py
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
"""Provides access to configuration information"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from ConfigParser import ConfigParser, NoOptionError, NoSectionError
|
||||||
|
|
||||||
|
class IdleConfParser(ConfigParser):
|
||||||
|
|
||||||
|
# these conf sections do not define extensions!
|
||||||
|
builtin_sections = {}
|
||||||
|
for section in ('EditorWindow', 'Colors'):
|
||||||
|
builtin_sections[section] = section
|
||||||
|
|
||||||
|
def getcolor(self, sec, name):
|
||||||
|
"""Return a dictionary with foreground and background colors
|
||||||
|
|
||||||
|
The return value is appropriate for passing to Tkinter in, e.g.,
|
||||||
|
a tag_config call.
|
||||||
|
"""
|
||||||
|
fore = self.getdef(sec, name + "-foreground")
|
||||||
|
back = self.getdef(sec, name + "-background")
|
||||||
|
return {"foreground": fore,
|
||||||
|
"background": back}
|
||||||
|
|
||||||
|
def getdef(self, sec, options, raw=0, vars=None, default=None):
|
||||||
|
"""Get an option value for given section or return default"""
|
||||||
|
try:
|
||||||
|
return self.get(sec, options, raw, vars)
|
||||||
|
except (NoSectionError, NoOptionError):
|
||||||
|
return default
|
||||||
|
|
||||||
|
def getsection(self, section):
|
||||||
|
"""Return a SectionConfigParser object"""
|
||||||
|
return SectionConfigParser(section, self)
|
||||||
|
|
||||||
|
def getextensions(self):
|
||||||
|
exts = []
|
||||||
|
for sec in self.sections():
|
||||||
|
if self.builtin_sections.has_key(sec):
|
||||||
|
continue
|
||||||
|
# enable is a bool, but it may not be defined
|
||||||
|
if self.getdef(sec, 'enable') != '0':
|
||||||
|
exts.append(sec)
|
||||||
|
return exts
|
||||||
|
|
||||||
|
def reload(self):
|
||||||
|
global idleconf
|
||||||
|
idleconf = IdleConfParser()
|
||||||
|
load(_dir) # _dir is a global holding the last directory loaded
|
||||||
|
|
||||||
|
class SectionConfigParser:
|
||||||
|
"""A ConfigParser object specialized for one section
|
||||||
|
|
||||||
|
This class has all the get methods that a regular ConfigParser does,
|
||||||
|
but without requiring a section argument.
|
||||||
|
"""
|
||||||
|
def __init__(self, section, config):
|
||||||
|
self.section = section
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
def options(self):
|
||||||
|
return self.config.options(self.section)
|
||||||
|
|
||||||
|
def get(self, options, raw=0, vars=None):
|
||||||
|
return self.config.get(self.section, options, raw, vars)
|
||||||
|
|
||||||
|
def getdef(self, options, raw=0, vars=None, default=None):
|
||||||
|
return self.config.getdef(self.section, options, raw, vars, default)
|
||||||
|
|
||||||
|
def getint(self, option):
|
||||||
|
return self.config.getint(self.section, option)
|
||||||
|
|
||||||
|
def getfloat(self, option):
|
||||||
|
return self.config.getint(self.section, option)
|
||||||
|
|
||||||
|
def getboolean(self, option):
|
||||||
|
return self.config.getint(self.section, option)
|
||||||
|
|
||||||
|
def getcolor(self, option):
|
||||||
|
return self.config.getcolor(self.section, option)
|
||||||
|
|
||||||
|
def load(dir):
|
||||||
|
"""Load IDLE configuration files based on IDLE install in dir
|
||||||
|
|
||||||
|
Attempts to load two config files:
|
||||||
|
dir/config.txt
|
||||||
|
dir/config-[win/mac/unix].txt
|
||||||
|
dir/config-%(sys.platform)s.txt
|
||||||
|
~/.idle
|
||||||
|
"""
|
||||||
|
global _dir
|
||||||
|
_dir = dir
|
||||||
|
|
||||||
|
if sys.platform[:3] == 'win':
|
||||||
|
genplatfile = os.path.join(dir, "config-win.txt")
|
||||||
|
# XXX don't know what the platform string is on a Mac
|
||||||
|
elif sys.platform[:3] == 'mac':
|
||||||
|
genplatfile = os.path.join(dir, "config-mac.txt")
|
||||||
|
else:
|
||||||
|
genplatfile = os.path.join(dir, "config-unix.txt")
|
||||||
|
|
||||||
|
platfile = os.path.join(dir, "config-%s.txt" % sys.platform)
|
||||||
|
|
||||||
|
try:
|
||||||
|
homedir = os.environ['HOME']
|
||||||
|
except KeyError:
|
||||||
|
homedir = os.getcwd()
|
||||||
|
|
||||||
|
idleconf.read((os.path.join(dir, "config.txt"), genplatfile, platfile,
|
||||||
|
os.path.join(homedir, ".idle")))
|
||||||
|
|
||||||
|
idleconf = IdleConfParser()
|
||||||
|
|
||||||
89
Lib/idlelib/IdleHistory.py
Normal file
89
Lib/idlelib/IdleHistory.py
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
import string
|
||||||
|
|
||||||
|
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 = string.split(self.text.get(start, end), self.output_sep)
|
||||||
|
return string.join(lines, "\n")
|
||||||
|
|
||||||
|
def _put_source(self, where, source):
|
||||||
|
output = string.join(string.split(source, "\n"), self.output_sep)
|
||||||
|
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 = string.strip(source)
|
||||||
|
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 = string.strip(s)
|
||||||
|
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")
|
||||||
|
|
||||||
138
Lib/idlelib/MultiScrolledLists.py
Normal file
138
Lib/idlelib/MultiScrolledLists.py
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import string
|
||||||
|
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()
|
||||||
32
Lib/idlelib/MultiStatusBar.py
Normal file
32
Lib/idlelib/MultiStatusBar.py
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
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()
|
||||||
130
Lib/idlelib/NEWS.txt
Normal file
130
Lib/idlelib/NEWS.txt
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
(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.
|
||||||
|
|
||||||
|
======================================================================
|
||||||
151
Lib/idlelib/ObjectBrowser.py
Normal file
151
Lib/idlelib/ObjectBrowser.py
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
# 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 1
|
||||||
|
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 1
|
||||||
|
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 Toplevel
|
||||||
|
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 = make_objecttreeitem("sys", sys)
|
||||||
|
node = TreeNode(sc.canvas, None, item)
|
||||||
|
node.expand()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test()
|
||||||
276
Lib/idlelib/OldStackViewer.py
Normal file
276
Lib/idlelib/OldStackViewer.py
Normal file
|
|
@ -0,0 +1,276 @@
|
||||||
|
import string
|
||||||
|
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 = string.strip(sourceline)
|
||||||
|
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()
|
||||||
279
Lib/idlelib/OutputWindow.py
Normal file
279
Lib/idlelib/OutputWindow.py
Normal file
|
|
@ -0,0 +1,279 @@
|
||||||
|
# changes by dscherer@cmu.edu
|
||||||
|
# - OutputWindow and OnDemandOutputWindow have been hastily
|
||||||
|
# extended to provide readline() support, an "iomark" separate
|
||||||
|
# from the "insert" cursor, and scrolling to clear the window.
|
||||||
|
# These changes are used by the ExecBinding module to provide
|
||||||
|
# standard input and output for user programs. Many of the new
|
||||||
|
# features are very similar to features of PyShell, which is a
|
||||||
|
# subclass of OutputWindow. Someone should make some sense of
|
||||||
|
# this.
|
||||||
|
|
||||||
|
from Tkinter import *
|
||||||
|
from EditorWindow import EditorWindow
|
||||||
|
import re
|
||||||
|
import tkMessageBox
|
||||||
|
|
||||||
|
from UndoDelegator import UndoDelegator
|
||||||
|
|
||||||
|
class OutputUndoDelegator(UndoDelegator):
|
||||||
|
reading = 0
|
||||||
|
# Forbid insert/delete before the I/O mark, in the blank lines after
|
||||||
|
# the output, or *anywhere* if we are not presently doing user input
|
||||||
|
def insert(self, index, chars, tags=None):
|
||||||
|
try:
|
||||||
|
if (self.delegate.compare(index, "<", "iomark") or
|
||||||
|
self.delegate.compare(index, ">", "endmark") or
|
||||||
|
(index!="iomark" and not self.reading)):
|
||||||
|
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") or
|
||||||
|
self.delegate.compare(index1, ">", "endmark") or
|
||||||
|
(index2 and self.delegate.compare(index2, ">=", "endmark")) or
|
||||||
|
not self.reading):
|
||||||
|
self.delegate.bell()
|
||||||
|
return
|
||||||
|
except TclError:
|
||||||
|
pass
|
||||||
|
UndoDelegator.delete(self, index1, index2)
|
||||||
|
|
||||||
|
class OutputWindow(EditorWindow):
|
||||||
|
"""An editor window that can serve as an input and output file.
|
||||||
|
The input support has been rather hastily hacked in, and should
|
||||||
|
not be trusted.
|
||||||
|
"""
|
||||||
|
|
||||||
|
UndoDelegator = OutputUndoDelegator
|
||||||
|
source_window = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **keywords):
|
||||||
|
if keywords.has_key('source_window'):
|
||||||
|
self.source_window = keywords['source_window']
|
||||||
|
apply(EditorWindow.__init__, (self,) + args)
|
||||||
|
self.text.bind("<<goto-file-line>>", self.goto_file_line)
|
||||||
|
self.text.bind("<<newline-and-indent>>", self.enter_callback)
|
||||||
|
self.text.mark_set("iomark","1.0")
|
||||||
|
self.text.mark_gravity("iomark", LEFT)
|
||||||
|
self.text.mark_set("endmark","1.0")
|
||||||
|
|
||||||
|
# Customize EditorWindow
|
||||||
|
|
||||||
|
def ispythonsource(self, filename):
|
||||||
|
# No colorization needed
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def short_title(self):
|
||||||
|
return "Output"
|
||||||
|
|
||||||
|
def long_title(self):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def maybesave(self):
|
||||||
|
# Override base class method -- don't ask any questions
|
||||||
|
if self.get_saved():
|
||||||
|
return "yes"
|
||||||
|
else:
|
||||||
|
return "no"
|
||||||
|
|
||||||
|
# Act as input file - incomplete
|
||||||
|
|
||||||
|
def set_line_and_column(self, event=None):
|
||||||
|
index = self.text.index(INSERT)
|
||||||
|
if (self.text.compare(index, ">", "endmark")):
|
||||||
|
self.text.mark_set("insert", "endmark")
|
||||||
|
self.text.see("insert")
|
||||||
|
EditorWindow.set_line_and_column(self)
|
||||||
|
|
||||||
|
reading = 0
|
||||||
|
canceled = 0
|
||||||
|
endoffile = 0
|
||||||
|
|
||||||
|
def readline(self):
|
||||||
|
save = self.reading
|
||||||
|
try:
|
||||||
|
self.reading = self.undo.reading = 1
|
||||||
|
self.text.mark_set("insert", "iomark")
|
||||||
|
self.text.see("insert")
|
||||||
|
self.top.mainloop()
|
||||||
|
finally:
|
||||||
|
self.reading = self.undo.reading = save
|
||||||
|
line = self.text.get("input", "iomark")
|
||||||
|
if self.canceled:
|
||||||
|
self.canceled = 0
|
||||||
|
raise KeyboardInterrupt
|
||||||
|
if self.endoffile:
|
||||||
|
self.endoffile = 0
|
||||||
|
return ""
|
||||||
|
return line or '\n'
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.interrupt()
|
||||||
|
return EditorWindow.close(self)
|
||||||
|
|
||||||
|
def interrupt(self):
|
||||||
|
if self.reading:
|
||||||
|
self.endoffile = 1
|
||||||
|
self.top.quit()
|
||||||
|
|
||||||
|
def enter_callback(self, event):
|
||||||
|
if self.reading and self.text.compare("insert", ">=", "iomark"):
|
||||||
|
self.text.mark_set("input", "iomark")
|
||||||
|
self.text.mark_set("iomark", "insert")
|
||||||
|
self.write('\n',"iomark")
|
||||||
|
self.text.tag_add("stdin", "input", "iomark")
|
||||||
|
self.text.update_idletasks()
|
||||||
|
self.top.quit() # Break out of recursive mainloop() in raw_input()
|
||||||
|
|
||||||
|
return "break"
|
||||||
|
|
||||||
|
# Act as output file
|
||||||
|
|
||||||
|
def write(self, s, tags=(), mark="iomark"):
|
||||||
|
self.text.mark_gravity(mark, RIGHT)
|
||||||
|
self.text.insert(mark, str(s), tags)
|
||||||
|
self.text.mark_gravity(mark, LEFT)
|
||||||
|
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.untitled(filename) or self.flist.open(filename)
|
||||||
|
edit.gotoline(lineno)
|
||||||
|
edit.wakeup()
|
||||||
|
|
||||||
|
def untitled(self, filename):
|
||||||
|
if filename!='Untitled' or not self.source_window or self.source_window.io.filename:
|
||||||
|
return None
|
||||||
|
return self.source_window
|
||||||
|
|
||||||
|
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)
|
||||||
|
if not self.untitled(filename):
|
||||||
|
try:
|
||||||
|
f = open(filename, "r")
|
||||||
|
f.close()
|
||||||
|
except IOError:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return filename, int(lineno)
|
||||||
|
except TypeError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# This classes now used by ExecBinding.py:
|
||||||
|
|
||||||
|
class OnDemandOutputWindow:
|
||||||
|
source_window = None
|
||||||
|
|
||||||
|
tagdefs = {
|
||||||
|
# XXX Should use IdlePrefs.ColorPrefs
|
||||||
|
"stdin": {"foreground": "black"},
|
||||||
|
"stdout": {"foreground": "blue"},
|
||||||
|
"stderr": {"foreground": "red"},
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, flist):
|
||||||
|
self.flist = flist
|
||||||
|
self.owin = None
|
||||||
|
self.title = "Output"
|
||||||
|
self.close_hook = None
|
||||||
|
self.old_close = None
|
||||||
|
|
||||||
|
def owclose(self):
|
||||||
|
if self.close_hook:
|
||||||
|
self.close_hook()
|
||||||
|
if self.old_close:
|
||||||
|
self.old_close()
|
||||||
|
|
||||||
|
def set_title(self, title):
|
||||||
|
self.title = title
|
||||||
|
if self.owin and self.owin.text:
|
||||||
|
self.owin.saved_change_hook()
|
||||||
|
|
||||||
|
def write(self, s, tags=(), mark="iomark"):
|
||||||
|
if not self.owin or not self.owin.text:
|
||||||
|
self.setup()
|
||||||
|
self.owin.write(s, tags, mark)
|
||||||
|
|
||||||
|
def readline(self):
|
||||||
|
if not self.owin or not self.owin.text:
|
||||||
|
self.setup()
|
||||||
|
return self.owin.readline()
|
||||||
|
|
||||||
|
def scroll_clear(self):
|
||||||
|
if self.owin and self.owin.text:
|
||||||
|
lineno = self.owin.getlineno("endmark")
|
||||||
|
self.owin.text.mark_set("insert","endmark")
|
||||||
|
self.owin.text.yview(float(lineno))
|
||||||
|
self.owin.wakeup()
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
self.owin = owin = OutputWindow(self.flist, source_window = self.source_window)
|
||||||
|
owin.short_title = lambda self=self: self.title
|
||||||
|
text = owin.text
|
||||||
|
|
||||||
|
self.old_close = owin.close_hook
|
||||||
|
owin.close_hook = self.owclose
|
||||||
|
|
||||||
|
# xxx Bad hack: 50 blank lines at the bottom so that
|
||||||
|
# we can scroll the top of the window to the output
|
||||||
|
# cursor in scroll_clear(). There must be a better way...
|
||||||
|
owin.text.mark_gravity('endmark', LEFT)
|
||||||
|
owin.text.insert('iomark', '\n'*50)
|
||||||
|
owin.text.mark_gravity('endmark', RIGHT)
|
||||||
|
|
||||||
|
for tag, cnf in self.tagdefs.items():
|
||||||
|
if cnf:
|
||||||
|
apply(text.tag_configure, (tag,), cnf)
|
||||||
|
text.tag_raise('sel')
|
||||||
192
Lib/idlelib/ParenMatch.py
Normal file
192
Lib/idlelib/ParenMatch.py
Normal file
|
|
@ -0,0 +1,192 @@
|
||||||
|
"""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 string
|
||||||
|
|
||||||
|
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 = string.count(buf[i:], "\n") - 1
|
||||||
|
# subtract one for the "\n" added to please the parser
|
||||||
|
upto_open = buf[:i]
|
||||||
|
j = string.rfind(upto_open, "\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
|
||||||
|
|
||||||
95
Lib/idlelib/PathBrowser.py
Normal file
95
Lib/idlelib/PathBrowser.py
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
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()
|
||||||
85
Lib/idlelib/Percolator.py
Normal file
85
Lib/idlelib/Percolator.py
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
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)
|
||||||
|
from Tkinter import *
|
||||||
|
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__":
|
||||||
|
main()
|
||||||
569
Lib/idlelib/PyParse.py
Normal file
569
Lib/idlelib/PyParse.py
Normal file
|
|
@ -0,0 +1,569 @@
|
||||||
|
import string
|
||||||
|
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(string.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
|
||||||
|
)
|
||||||
|
\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 = string.join(_tran, '')
|
||||||
|
del ch
|
||||||
|
|
||||||
|
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'
|
||||||
|
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,
|
||||||
|
_rfind=string.rfind,
|
||||||
|
_synchre=_synchre):
|
||||||
|
str, pos = self.str, None
|
||||||
|
if use_ps1:
|
||||||
|
# shell window
|
||||||
|
ps1 = '\n' + sys.ps1
|
||||||
|
i = _rfind(str, 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 = _rfind(str, ":\n", 0, limit)
|
||||||
|
if i < 0:
|
||||||
|
break
|
||||||
|
i = _rfind(str, '\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, _replace=string.replace, _find=string.find):
|
||||||
|
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 = string.translate(str, _tran)
|
||||||
|
str = _replace(str, 'xxxxxxxx', 'x')
|
||||||
|
str = _replace(str, 'xxxx', 'x')
|
||||||
|
str = _replace(str, 'xx', 'x')
|
||||||
|
str = _replace(str, 'xx', 'x')
|
||||||
|
str = _replace(str, '\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 = _find(str, '\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, _rfind=string.rfind, _find=string.find,
|
||||||
|
_ws=string.whitespace):
|
||||||
|
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 = _rfind(str, '\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
|
||||||
|
p = m.end()
|
||||||
|
# back up over totally boring whitespace
|
||||||
|
i = p-1 # index of last boring char
|
||||||
|
while i >= 0 and str[i] in " \t\n":
|
||||||
|
i = i-1
|
||||||
|
if i >= 0:
|
||||||
|
lastch = str[i]
|
||||||
|
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 = _find(str, '\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, _find=string.find):
|
||||||
|
self._study2()
|
||||||
|
assert self.continuation == C_BRACKET
|
||||||
|
j = self.lastopenbracketpos
|
||||||
|
str = self.str
|
||||||
|
n = len(str)
|
||||||
|
origi = i = string.rfind(str, '\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 = _find(str, '\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(string.expandtabs(str[i:j],
|
||||||
|
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 = string.find(str, '\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(string.expandtabs(str[self.stmt_start :
|
||||||
|
i],
|
||||||
|
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
|
||||||
860
Lib/idlelib/PyShell.py
Normal file
860
Lib/idlelib/PyShell.py
Normal file
|
|
@ -0,0 +1,860 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
|
||||||
|
# changes by dscherer@cmu.edu
|
||||||
|
|
||||||
|
# the main() function has been replaced by a whole class, in order to
|
||||||
|
# address the constraint that only one process can sit on the port
|
||||||
|
# hard-coded into the loader.
|
||||||
|
|
||||||
|
# It attempts to load the RPC protocol server and publish itself. If
|
||||||
|
# that fails, it assumes that some other copy of IDLE is already running
|
||||||
|
# on the port and attempts to contact it. It then uses the RPC mechanism
|
||||||
|
# to ask that copy to do whatever it was instructed (via the command
|
||||||
|
# line) to do. (Think netscape -remote). The handling of command line
|
||||||
|
# arguments for remotes is still very incomplete.
|
||||||
|
|
||||||
|
# default behavior (no command line options) is to NOT start the Python
|
||||||
|
# Shell. If files are specified, they are opened, otherwise a single
|
||||||
|
# blank editor window opens.
|
||||||
|
|
||||||
|
# If any command line -options are specified, a shell does appear. This
|
||||||
|
# is necessary to make the current semantics of the options make sense.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import spawn
|
||||||
|
import sys
|
||||||
|
import string
|
||||||
|
import getopt
|
||||||
|
import re
|
||||||
|
import protocol
|
||||||
|
|
||||||
|
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, OnDemandOutputWindow
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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
|
||||||
|
return InteractiveInterpreter.runsource(self, source, filename)
|
||||||
|
|
||||||
|
def stuffsource(self, source):
|
||||||
|
# Stuff source in the filename cache
|
||||||
|
filename = "<pyshell#%d>" % self.gid
|
||||||
|
self.gid = self.gid + 1
|
||||||
|
lines = string.split(source, "\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 string.letters + string.digits + "_":
|
||||||
|
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
|
||||||
|
text = self.tkconsole.text
|
||||||
|
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
|
||||||
|
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 PyShellEditorWindow.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 1
|
||||||
|
|
||||||
|
def short_title(self):
|
||||||
|
return self.shell_title
|
||||||
|
|
||||||
|
def begin(self):
|
||||||
|
self.resetoutput()
|
||||||
|
self.write("Python %s on %s\n%s\nIDLE %s -- press F1 for help\n" %
|
||||||
|
(sys.version, sys.platform, sys.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 1
|
||||||
|
|
||||||
|
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 string.strip(s):
|
||||||
|
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 1
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class usageError:
|
||||||
|
def __init__(self, string): self.string = string
|
||||||
|
def __repr__(self): return self.string
|
||||||
|
|
||||||
|
class main:
|
||||||
|
def __init__(self):
|
||||||
|
try:
|
||||||
|
self.server = protocol.Server(connection_hook = self.address_ok)
|
||||||
|
protocol.publish( 'IDLE', self.connect )
|
||||||
|
self.main( sys.argv[1:] )
|
||||||
|
return
|
||||||
|
except protocol.connectionLost:
|
||||||
|
try:
|
||||||
|
client = protocol.Client()
|
||||||
|
IDLE = client.getobject('IDLE')
|
||||||
|
if IDLE:
|
||||||
|
try:
|
||||||
|
IDLE.remote( sys.argv[1:] )
|
||||||
|
except usageError, msg:
|
||||||
|
sys.stderr.write("Error: %s\n" % str(msg))
|
||||||
|
sys.stderr.write(usage_msg)
|
||||||
|
return
|
||||||
|
except protocol.connectionLost:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# xxx Should scream via Tk()
|
||||||
|
print "Something already has our socket, but it won't open a window for me!"
|
||||||
|
print "Unable to proceed."
|
||||||
|
|
||||||
|
def idle(self):
|
||||||
|
spawn.kill_zombies()
|
||||||
|
self.server.rpc_loop()
|
||||||
|
root.after(25, self.idle)
|
||||||
|
|
||||||
|
# We permit connections from localhost only
|
||||||
|
def address_ok(self, addr):
|
||||||
|
return addr[0] == '127.0.0.1'
|
||||||
|
|
||||||
|
def connect(self, client, addr):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def remote( self, argv ):
|
||||||
|
# xxx Should make this behavior match the behavior in main, or redo
|
||||||
|
# command line options entirely.
|
||||||
|
|
||||||
|
try:
|
||||||
|
opts, args = getopt.getopt(argv, "c:deist:")
|
||||||
|
except getopt.error, msg:
|
||||||
|
raise usageError(msg)
|
||||||
|
|
||||||
|
for filename in args:
|
||||||
|
flist.open(filename)
|
||||||
|
if not args:
|
||||||
|
flist.new()
|
||||||
|
|
||||||
|
def main( self, argv ):
|
||||||
|
cmd = None
|
||||||
|
edit = 0
|
||||||
|
noshell = 1
|
||||||
|
|
||||||
|
debug = 0
|
||||||
|
startup = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
opts, args = getopt.getopt(argv, "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:
|
||||||
|
noshell = 0
|
||||||
|
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
|
||||||
|
|
||||||
|
if noshell: edit=1
|
||||||
|
|
||||||
|
if not edit:
|
||||||
|
if cmd:
|
||||||
|
sys.argv = ["-c"] + args
|
||||||
|
else:
|
||||||
|
sys.argv = args or [""]
|
||||||
|
|
||||||
|
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()
|
||||||
|
fixwordbreaks(root)
|
||||||
|
root.withdraw()
|
||||||
|
flist = PyShellFileList(root)
|
||||||
|
|
||||||
|
if edit:
|
||||||
|
for filename in args:
|
||||||
|
flist.open(filename)
|
||||||
|
if not args:
|
||||||
|
flist.new()
|
||||||
|
|
||||||
|
#dbg=OnDemandOutputWindow(flist)
|
||||||
|
#dbg.set_title('Internal IDLE Problem')
|
||||||
|
#sys.stdout = PseudoFile(dbg,['stdout'])
|
||||||
|
#sys.stderr = PseudoFile(dbg,['stderr'])
|
||||||
|
|
||||||
|
if noshell:
|
||||||
|
flist.pyshell = None
|
||||||
|
else:
|
||||||
|
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()
|
||||||
|
|
||||||
|
self.idle()
|
||||||
|
root.mainloop()
|
||||||
|
root.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
121
Lib/idlelib/README.txt
Normal file
121
Lib/idlelib/README.txt
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
EXPERIMENTAL LOADER IDLE 2000-05-29
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
David Scherer <dscherer@cmu.edu>
|
||||||
|
|
||||||
|
This is a modification of the CVS version of IDLE 0.5, updated as of
|
||||||
|
2000-03-09. It is alpha software and might be unstable. If it breaks,
|
||||||
|
you get to keep both pieces.
|
||||||
|
|
||||||
|
If you have problems or suggestions, you should either contact me or
|
||||||
|
post to the list at http://www.python.org/mailman/listinfo/idle-dev
|
||||||
|
(making it clear that you are using this modified version of IDLE).
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
The ExecBinding module, a replacement for ScriptBinding, executes
|
||||||
|
programs in a separate process, piping standard I/O through an RPC
|
||||||
|
mechanism to an OnDemandOutputWindow in IDLE. It supports executing
|
||||||
|
unnamed programs (through a temporary file). It does not yet support
|
||||||
|
debugging.
|
||||||
|
|
||||||
|
When running programs with ExecBinding, tracebacks will be clipped
|
||||||
|
to exclude system modules. If, however, a system module calls back
|
||||||
|
into the user program, that part of the traceback will be shown.
|
||||||
|
|
||||||
|
The OnDemandOutputWindow class has been improved. In particular,
|
||||||
|
it now supports a readline() function used to implement user input,
|
||||||
|
and a scroll_clear() operation which is used to hide the output of
|
||||||
|
a previous run by scrolling it out of the window.
|
||||||
|
|
||||||
|
Startup behavior has been changed. By default IDLE starts up with
|
||||||
|
just a blank editor window, rather than an interactive window. Opening
|
||||||
|
a file in such a blank window replaces the (nonexistent) contents of
|
||||||
|
that window instead of creating another window. Because of the need to
|
||||||
|
have a well-known port for the ExecBinding protocol, only one copy of
|
||||||
|
IDLE can be running. Additional invocations use the RPC mechanism to
|
||||||
|
report their command line arguments to the copy already running.
|
||||||
|
|
||||||
|
The menus have been reorganized. In particular, the excessively large
|
||||||
|
'edit' menu has been split up into 'edit', 'format', and 'run'.
|
||||||
|
|
||||||
|
'Python Documentation' now works on Windows, if the win32api module is
|
||||||
|
present.
|
||||||
|
|
||||||
|
A few key bindings have been changed: F1 now loads Python Documentation
|
||||||
|
instead of the IDLE help; shift-TAB is now a synonym for unindent.
|
||||||
|
|
||||||
|
New modules:
|
||||||
|
ExecBinding.py Executes program through loader
|
||||||
|
loader.py Bootstraps user program
|
||||||
|
protocol.py RPC protocol
|
||||||
|
Remote.py User-process interpreter
|
||||||
|
spawn.py OS-specific code to start programs
|
||||||
|
|
||||||
|
Files modified:
|
||||||
|
autoindent.py ( bindings tweaked )
|
||||||
|
bindings.py ( menus reorganized )
|
||||||
|
config.txt ( execbinding enabled )
|
||||||
|
editorwindow.py ( new menus, fixed 'Python Documentation' )
|
||||||
|
filelist.py ( hook for "open in same window" )
|
||||||
|
formatparagraph.py ( bindings tweaked )
|
||||||
|
idle.bat ( removed absolute pathname )
|
||||||
|
idle.pyw ( weird bug due to import with same name? )
|
||||||
|
iobinding.py ( open in same window, EOL convention )
|
||||||
|
keydefs.py ( bindings tweaked )
|
||||||
|
outputwindow.py ( readline, scroll_clear, etc )
|
||||||
|
pyshell.py ( changed startup behavior )
|
||||||
|
readme.txt ( <Recursion on file with id=1234567> )
|
||||||
|
|
||||||
|
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, so it is currently only usable with a
|
||||||
|
Python 1.5.2 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
|
||||||
|
|
||||||
|
(removed, since Guido probably doesn't want complaints about my
|
||||||
|
changes)
|
||||||
|
|
||||||
|
--Guido van Rossum (home page: http://www.python.org/~guido/)
|
||||||
101
Lib/idlelib/Remote.py
Normal file
101
Lib/idlelib/Remote.py
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
"""Remote
|
||||||
|
This module is imported by the loader and serves to control
|
||||||
|
the execution of the user program. It presently executes files
|
||||||
|
and reports exceptions to IDLE. It could be extended to provide
|
||||||
|
other services, such as interactive mode and debugging. To that
|
||||||
|
end, it could be a subclass of e.g. InteractiveInterpreter.
|
||||||
|
|
||||||
|
Two other classes, pseudoIn and pseudoOut, are file emulators also
|
||||||
|
used by loader.
|
||||||
|
"""
|
||||||
|
import sys, os
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
class Remote:
|
||||||
|
def __init__(self, main, master):
|
||||||
|
self.main = main
|
||||||
|
self.master = master
|
||||||
|
self.this_file = self.canonic( self.__init__.im_func.func_code.co_filename )
|
||||||
|
|
||||||
|
def canonic(self, path):
|
||||||
|
return os.path.normcase(os.path.abspath(path))
|
||||||
|
|
||||||
|
def mainloop(self):
|
||||||
|
while 1:
|
||||||
|
args = self.master.get_command()
|
||||||
|
|
||||||
|
try:
|
||||||
|
f = getattr(self,args[0])
|
||||||
|
apply(f,args[1:])
|
||||||
|
except:
|
||||||
|
if not self.report_exception(): raise
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
def run(self, *argv):
|
||||||
|
sys.argv = argv
|
||||||
|
|
||||||
|
path = self.canonic( argv[0] )
|
||||||
|
dir = self.dir = os.path.dirname(path)
|
||||||
|
os.chdir(dir)
|
||||||
|
|
||||||
|
sys.path[0] = dir
|
||||||
|
|
||||||
|
usercode = open(path)
|
||||||
|
exec usercode in self.main
|
||||||
|
|
||||||
|
def report_exception(self):
|
||||||
|
try:
|
||||||
|
type, value, tb = sys.exc_info()
|
||||||
|
sys.last_type = type
|
||||||
|
sys.last_value = value
|
||||||
|
sys.last_traceback = tb
|
||||||
|
|
||||||
|
tblist = traceback.extract_tb(tb)
|
||||||
|
|
||||||
|
# Look through the traceback, canonicalizing filenames and
|
||||||
|
# eliminating leading and trailing system modules.
|
||||||
|
first = last = 1
|
||||||
|
for i in range(len(tblist)):
|
||||||
|
filename, lineno, name, line = tblist[i]
|
||||||
|
filename = self.canonic(filename)
|
||||||
|
tblist[i] = filename, lineno, name, line
|
||||||
|
|
||||||
|
dir = os.path.dirname(filename)
|
||||||
|
if filename == self.this_file:
|
||||||
|
first = i+1
|
||||||
|
elif dir==self.dir:
|
||||||
|
last = i+1
|
||||||
|
|
||||||
|
# Canonicalize the filename in a syntax error, too:
|
||||||
|
if type is SyntaxError:
|
||||||
|
try:
|
||||||
|
msg, (filename, lineno, offset, line) = value
|
||||||
|
filename = self.canonic(filename)
|
||||||
|
value = msg, (filename, lineno, offset, line)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return self.master.program_exception( type, value, tblist, first, last )
|
||||||
|
finally:
|
||||||
|
# avoid any circular reference through the traceback
|
||||||
|
del tb
|
||||||
|
|
||||||
|
class pseudoIn:
|
||||||
|
def __init__(self, readline):
|
||||||
|
self.readline = readline
|
||||||
|
def isatty():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
class pseudoOut:
|
||||||
|
def __init__(self, func, **kw):
|
||||||
|
self.func = func
|
||||||
|
self.kw = kw
|
||||||
|
def write(self, *args):
|
||||||
|
return apply( self.func, args, self.kw )
|
||||||
|
def writelines(self, l):
|
||||||
|
map(self.write, l)
|
||||||
|
def flush(self):
|
||||||
|
pass
|
||||||
|
|
||||||
172
Lib/idlelib/ReplaceDialog.py
Normal file
172
Lib/idlelib/ReplaceDialog.py
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
import string
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import fnmatch
|
||||||
|
from Tkinter import *
|
||||||
|
import tkMessageBox
|
||||||
|
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 = re.pcre_expand(m, 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 0
|
||||||
|
text = self.text
|
||||||
|
res = self.engine.search_text(text, None, ok)
|
||||||
|
if not res:
|
||||||
|
text.bell()
|
||||||
|
return 0
|
||||||
|
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 1
|
||||||
|
|
||||||
|
def do_replace(self):
|
||||||
|
prog = self.engine.getprog()
|
||||||
|
if not prog:
|
||||||
|
return 0
|
||||||
|
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 0
|
||||||
|
new = re.pcre_expand(m, 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 1
|
||||||
|
|
||||||
|
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")
|
||||||
169
Lib/idlelib/ScriptBinding.py
Normal file
169
Lib/idlelib/ScriptBinding.py
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
"""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):
|
||||||
|
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
|
||||||
|
tabnanny.reset_globals()
|
||||||
|
f = open(filename, 'r')
|
||||||
|
try:
|
||||||
|
tokenize.tokenize(f.readline, tabnanny.tokeneater)
|
||||||
|
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():
|
||||||
|
self.errorbox("Not saved",
|
||||||
|
"Please save 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()
|
||||||
139
Lib/idlelib/ScrolledList.py
Normal file
139
Lib/idlelib/ScrolledList.py
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
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()
|
||||||
97
Lib/idlelib/SearchBinding.py
Normal file
97
Lib/idlelib/SearchBinding.py
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
import tkSimpleDialog
|
||||||
|
|
||||||
|
###$ event <<find>>
|
||||||
|
###$ win <Control-f>
|
||||||
|
###$ unix <Control-u><Control-u><Control-s>
|
||||||
|
|
||||||
|
###$ event <<find-again>>
|
||||||
|
###$ win <Control-g>
|
||||||
|
###$ win <F3>
|
||||||
|
###$ unix <Control-u><Control-s>
|
||||||
|
|
||||||
|
###$ event <<find-selection>>
|
||||||
|
###$ win <Control-F3>
|
||||||
|
###$ unix <Control-s>
|
||||||
|
|
||||||
|
###$ event <<find-in-files>>
|
||||||
|
###$ win <Alt-F3>
|
||||||
|
|
||||||
|
###$ event <<replace>>
|
||||||
|
###$ win <Control-h>
|
||||||
|
|
||||||
|
###$ event <<goto-line>>
|
||||||
|
###$ win <Alt-g>
|
||||||
|
###$ unix <Alt-g>
|
||||||
|
|
||||||
|
class SearchBinding:
|
||||||
|
|
||||||
|
windows_keydefs = {
|
||||||
|
'<<find-again>>': ['<Control-g>', '<F3>'],
|
||||||
|
'<<find-in-files>>': ['<Alt-F3>'],
|
||||||
|
'<<find-selection>>': ['<Control-F3>'],
|
||||||
|
'<<find>>': ['<Control-f>'],
|
||||||
|
'<<replace>>': ['<Control-h>'],
|
||||||
|
'<<goto-line>>': ['<Alt-g>'],
|
||||||
|
}
|
||||||
|
|
||||||
|
unix_keydefs = {
|
||||||
|
'<<find-again>>': ['<Control-u><Control-s>'],
|
||||||
|
'<<find-in-files>>': ['<Alt-s>', '<Meta-s>'],
|
||||||
|
'<<find-selection>>': ['<Control-s>'],
|
||||||
|
'<<find>>': ['<Control-u><Control-u><Control-s>'],
|
||||||
|
'<<replace>>': ['<Control-r>'],
|
||||||
|
'<<goto-line>>': ['<Alt-g>', '<Meta-g>'],
|
||||||
|
}
|
||||||
|
|
||||||
|
menudefs = [
|
||||||
|
('edit', [
|
||||||
|
None,
|
||||||
|
('_Find...', '<<find>>'),
|
||||||
|
('Find a_gain', '<<find-again>>'),
|
||||||
|
('Find _selection', '<<find-selection>>'),
|
||||||
|
('Find in Files...', '<<find-in-files>>'),
|
||||||
|
('R_eplace...', '<<replace>>'),
|
||||||
|
('Go to _line', '<<goto-line>>'),
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, editwin):
|
||||||
|
self.editwin = editwin
|
||||||
|
|
||||||
|
def find_event(self, event):
|
||||||
|
import SearchDialog
|
||||||
|
SearchDialog.find(self.editwin.text)
|
||||||
|
return "break"
|
||||||
|
|
||||||
|
def find_again_event(self, event):
|
||||||
|
import SearchDialog
|
||||||
|
SearchDialog.find_again(self.editwin.text)
|
||||||
|
return "break"
|
||||||
|
|
||||||
|
def find_selection_event(self, event):
|
||||||
|
import SearchDialog
|
||||||
|
SearchDialog.find_selection(self.editwin.text)
|
||||||
|
return "break"
|
||||||
|
|
||||||
|
def find_in_files_event(self, event):
|
||||||
|
import GrepDialog
|
||||||
|
GrepDialog.grep(self.editwin.text, self.editwin.io, self.editwin.flist)
|
||||||
|
return "break"
|
||||||
|
|
||||||
|
def replace_event(self, event):
|
||||||
|
import ReplaceDialog
|
||||||
|
ReplaceDialog.replace(self.editwin.text)
|
||||||
|
return "break"
|
||||||
|
|
||||||
|
def goto_line_event(self, event):
|
||||||
|
text = self.editwin.text
|
||||||
|
lineno = tkSimpleDialog.askinteger("Goto",
|
||||||
|
"Go to line number:",
|
||||||
|
parent=text)
|
||||||
|
if lineno is None:
|
||||||
|
return "break"
|
||||||
|
if lineno <= 0:
|
||||||
|
text.bell()
|
||||||
|
return "break"
|
||||||
|
text.mark_set("insert", "%d.0" % lineno)
|
||||||
|
text.see("insert")
|
||||||
67
Lib/idlelib/SearchDialog.py
Normal file
67
Lib/idlelib/SearchDialog.py
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
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 0
|
||||||
|
if not self.engine.getprog():
|
||||||
|
return 0
|
||||||
|
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 0
|
||||||
|
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 1
|
||||||
|
else:
|
||||||
|
text.bell()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def find_selection(self, text):
|
||||||
|
pat = text.get("sel.first", "sel.last")
|
||||||
|
if pat:
|
||||||
|
self.engine.setcookedpat(pat)
|
||||||
|
return self.find_again(text)
|
||||||
129
Lib/idlelib/SearchDialogBase.py
Normal file
129
Lib/idlelib/SearchDialogBase.py
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
import string
|
||||||
|
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, col=0, sticky="w")
|
||||||
|
e = Entry(self.top, textvariable=var, exportselection=0)
|
||||||
|
e.grid(row=self.row, col=1, sticky="we")
|
||||||
|
self.row = self.row + 1
|
||||||
|
return e
|
||||||
|
|
||||||
|
def make_frame(self):
|
||||||
|
f = Frame(self.top)
|
||||||
|
f.grid(row=self.row, col=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()
|
||||||
221
Lib/idlelib/SearchEngine.py
Normal file
221
Lib/idlelib/SearchEngine.py
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
import string
|
||||||
|
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, string.split(pos, "."))
|
||||||
|
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, string.split(index, ".")) # Fails on invalid index
|
||||||
|
return line, col
|
||||||
92
Lib/idlelib/Separator.py
Normal file
92
Lib/idlelib/Separator.py
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
from Tkinter import *
|
||||||
|
|
||||||
|
class Separator:
|
||||||
|
|
||||||
|
def __init__(self, master, orient, min=10, thickness=5, bg=None):
|
||||||
|
self.min = max(1, min)
|
||||||
|
self.thickness = max(1, thickness)
|
||||||
|
if orient in ("h", "horizontal"):
|
||||||
|
self.side = "left"
|
||||||
|
self.dim = "width"
|
||||||
|
self.dir = "x"
|
||||||
|
self.cursor = "sb_h_double_arrow"
|
||||||
|
elif orient in ("v", "vertical"):
|
||||||
|
self.side = "top"
|
||||||
|
self.dim = "height"
|
||||||
|
self.dir = "y"
|
||||||
|
self.cursor = "sb_v_double_arrow"
|
||||||
|
else:
|
||||||
|
raise ValueError, "Separator: orient should be h or v"
|
||||||
|
self.winfo_dim = "winfo_" + self.dim
|
||||||
|
self.master = master = Frame(master)
|
||||||
|
master.pack(expand=1, fill="both")
|
||||||
|
self.f1 = Frame(master)
|
||||||
|
self.f1.pack(expand=1, fill="both", side=self.side)
|
||||||
|
self.div = Frame(master, cursor=self.cursor)
|
||||||
|
self.div[self.dim] = self.thickness
|
||||||
|
self.div.pack(fill="both", side=self.side)
|
||||||
|
self.f2 = Frame(master)
|
||||||
|
self.f2.pack(expand=1, fill="both", side=self.side)
|
||||||
|
self.div.bind("<ButtonPress-1>", self.divider_press)
|
||||||
|
if bg:
|
||||||
|
##self.f1["bg"] = bg
|
||||||
|
##self.f2["bg"] = bg
|
||||||
|
self.div["bg"] = bg
|
||||||
|
|
||||||
|
def parts(self):
|
||||||
|
return self.f1, self.f2
|
||||||
|
|
||||||
|
def divider_press(self, event):
|
||||||
|
self.press_event = event
|
||||||
|
self.f1.pack_propagate(0)
|
||||||
|
self.f2.pack_propagate(0)
|
||||||
|
for f in self.f1, self.f2:
|
||||||
|
for dim in "width", "height":
|
||||||
|
f[dim] = getattr(f, "winfo_"+dim)()
|
||||||
|
self.div.bind("<Motion>", self.div_motion)
|
||||||
|
self.div.bind("<ButtonRelease-1>", self.div_release)
|
||||||
|
self.div.grab_set()
|
||||||
|
|
||||||
|
def div_motion(self, event):
|
||||||
|
delta = getattr(event, self.dir) - getattr(self.press_event, self.dir)
|
||||||
|
if delta:
|
||||||
|
dim1 = getattr(self.f1, self.winfo_dim)()
|
||||||
|
dim2 = getattr(self.f2, self.winfo_dim)()
|
||||||
|
delta = max(delta, self.min-dim1)
|
||||||
|
delta = min(delta, dim2-self.min)
|
||||||
|
dim1 = dim1 + delta
|
||||||
|
dim2 = dim2 - delta
|
||||||
|
self.f1[self.dim] = dim1
|
||||||
|
self.f2[self.dim] = dim2
|
||||||
|
|
||||||
|
def div_release(self, event):
|
||||||
|
self.div_motion(event)
|
||||||
|
self.div.unbind("<Motion>")
|
||||||
|
self.div.grab_release()
|
||||||
|
|
||||||
|
class VSeparator(Separator):
|
||||||
|
|
||||||
|
def __init__(self, master, min=10, thickness=5, bg=None):
|
||||||
|
Separator.__init__(self, master, "v", min, thickness, bg)
|
||||||
|
|
||||||
|
class HSeparator(Separator):
|
||||||
|
|
||||||
|
def __init__(self, master, min=10, thickness=5, bg=None):
|
||||||
|
Separator.__init__(self, master, "h", min, thickness, bg)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
root = Tk()
|
||||||
|
tlist = []
|
||||||
|
outer = HSeparator(root, bg="red")
|
||||||
|
for part in outer.parts():
|
||||||
|
inner = VSeparator(part, bg="blue")
|
||||||
|
for f in inner.parts():
|
||||||
|
t = Text(f, width=40, height=10, borderwidth=0)
|
||||||
|
t.pack(fill="both", expand=1)
|
||||||
|
tlist.append(t)
|
||||||
|
tlist[0].insert("1.0", "Make your own Mondrian!")
|
||||||
|
tlist[1].insert("1.0", "Move the colored dividers...")
|
||||||
|
root.mainloop()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
135
Lib/idlelib/StackViewer.py
Normal file
135
Lib/idlelib/StackViewer.py
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
import string
|
||||||
|
from Tkinter import *
|
||||||
|
import linecache
|
||||||
|
|
||||||
|
from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
|
||||||
|
from ObjectBrowser import ObjectTreeItem, make_objecttreeitem
|
||||||
|
from OldStackViewer import StackViewer, NamespaceViewer
|
||||||
|
|
||||||
|
def StackBrowser(root, flist=None, stack=None):
|
||||||
|
top = Toplevel(root)
|
||||||
|
sc = ScrolledCanvas(top, bg="white", highlightthickness=0)
|
||||||
|
sc.frame.pack(expand=1, fill="both")
|
||||||
|
item = StackTreeItem(flist)
|
||||||
|
node = TreeNode(sc.canvas, None, item)
|
||||||
|
node.expand()
|
||||||
|
|
||||||
|
class StackTreeItem(TreeItem):
|
||||||
|
|
||||||
|
def __init__(self, flist=None):
|
||||||
|
self.flist = flist
|
||||||
|
self.stack = get_stack()
|
||||||
|
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 = string.strip(sourceline)
|
||||||
|
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
|
||||||
|
edit = self.flist.open(filename)
|
||||||
|
edit.gotoline(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
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
root = Tk()
|
||||||
|
root.withdraw()
|
||||||
|
StackBrowser(root)
|
||||||
205
Lib/idlelib/TODO.txt
Normal file
205
Lib/idlelib/TODO.txt
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
|
||||||
|
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
|
||||||
|
- 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)
|
||||||
87
Lib/idlelib/ToolTip.py
Normal file
87
Lib/idlelib/ToolTip.py
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
# Ideas gleaned from PySol
|
||||||
|
|
||||||
|
import os
|
||||||
|
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()
|
||||||
471
Lib/idlelib/TreeWidget.py
Normal file
471
Lib/idlelib/TreeWidget.py
Normal file
|
|
@ -0,0 +1,471 @@
|
||||||
|
# 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
|
||||||
|
import string
|
||||||
|
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 = TreeNode(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):
|
||||||
|
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()
|
||||||
352
Lib/idlelib/UndoDelegator.py
Normal file
352
Lib/idlelib/UndoDelegator.py
Normal file
|
|
@ -0,0 +1,352 @@
|
||||||
|
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.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" + string.join(strs, ",\n") + "\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()
|
||||||
92
Lib/idlelib/WidgetRedirector.py
Normal file
92
Lib/idlelib/WidgetRedirector.py
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
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()
|
||||||
85
Lib/idlelib/WindowList.py
Normal file
85
Lib/idlelib/WindowList.py
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
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
|
||||||
46
Lib/idlelib/ZoomHeight.py
Normal file
46
Lib/idlelib/ZoomHeight.py
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
# 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)
|
||||||
1
Lib/idlelib/__init__.py
Normal file
1
Lib/idlelib/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
# Dummy file to make this a potential package.
|
||||||
3
Lib/idlelib/config-unix.txt
Normal file
3
Lib/idlelib/config-unix.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[EditorWindow]
|
||||||
|
font-name= courier
|
||||||
|
font-size= 10
|
||||||
3
Lib/idlelib/config-win.txt
Normal file
3
Lib/idlelib/config-win.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[EditorWindow]
|
||||||
|
font-name: courier new
|
||||||
|
font-size: 10
|
||||||
66
Lib/idlelib/config.txt
Normal file
66
Lib/idlelib/config.txt
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
# 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 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] # disabled in favor of ExecBinding
|
||||||
|
|
||||||
|
[ExecBinding]
|
||||||
|
|
||||||
|
[CallTips]
|
||||||
|
|
||||||
|
[ParenMatch]
|
||||||
|
enable= 0
|
||||||
|
style= expression
|
||||||
|
flash-delay= 500
|
||||||
|
bell= 1
|
||||||
|
hilite-foreground= black
|
||||||
|
hilite-background= #43cd80
|
||||||
93
Lib/idlelib/eventparse.py
Normal file
93
Lib/idlelib/eventparse.py
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
|
||||||
|
"""Parse event definitions out of comments in source files."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import string
|
||||||
|
import getopt
|
||||||
|
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(string.strip(line[2:-1]))
|
||||||
|
else:
|
||||||
|
if sublist:
|
||||||
|
hits.append(sublist)
|
||||||
|
sublist = []
|
||||||
|
if sublist:
|
||||||
|
hits.append(sublist)
|
||||||
|
sublist = []
|
||||||
|
dd = {}
|
||||||
|
for sublist in hits:
|
||||||
|
d = {}
|
||||||
|
for line in sublist:
|
||||||
|
words = string.split(line, 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())
|
||||||
106
Lib/idlelib/extend.txt
Normal file
106
Lib/idlelib/extend.txt
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
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 extend.py; 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 "extend.py", which contains a
|
||||||
|
simple table used to configure the loading of extensions. This file
|
||||||
|
currently contains a single list variable named "standard", which is a
|
||||||
|
list of extension names that are to be loaded. (In the future, other
|
||||||
|
configuration variables may be added to this module.)
|
||||||
|
|
||||||
|
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.
|
||||||
155
Lib/idlelib/help.txt
Normal file
155
Lib/idlelib/help.txt
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
[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 ColorPrefs class in IdlePrefs.py.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
To change the font on Windows, open EditorWindow.py and change
|
||||||
|
text['font'] = ("lucida console", 8)
|
||||||
|
to, e.g.,
|
||||||
|
text['font'] = ("courier new", 10)
|
||||||
|
|
||||||
|
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.
|
||||||
3
Lib/idlelib/idle.bat
Executable file
3
Lib/idlelib/idle.bat
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
@echo off
|
||||||
|
rem Working IDLE bat for Windows - uses start instead of absolute pathname
|
||||||
|
start idle.pyw %1 %2 %3 %4 %5 %6 %7 %8 %9
|
||||||
12
Lib/idlelib/idle.py
Normal file
12
Lib/idlelib/idle.py
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import IdleConf
|
||||||
|
|
||||||
|
idle_dir = os.path.split(sys.argv[0])[0]
|
||||||
|
IdleConf.load(idle_dir)
|
||||||
|
|
||||||
|
# defer importing Pyshell until IdleConf is loaded
|
||||||
|
import PyShell
|
||||||
|
PyShell.main()
|
||||||
12
Lib/idlelib/idle.pyw
Normal file
12
Lib/idlelib/idle.pyw
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import IdleConf
|
||||||
|
|
||||||
|
idle_dir = os.path.split(sys.argv[0])[0]
|
||||||
|
IdleConf.load(idle_dir)
|
||||||
|
|
||||||
|
# defer importing Pyshell until IdleConf is loaded
|
||||||
|
import PyShell
|
||||||
|
PyShell.main()
|
||||||
1
Lib/idlelib/idlever.py
Normal file
1
Lib/idlelib/idlever.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
IDLE_VERSION = "0.5"
|
||||||
55
Lib/idlelib/keydefs.py
Normal file
55
Lib/idlelib/keydefs.py
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
windows_keydefs = \
|
||||||
|
{'<<Copy>>': ['<Control-c>'],
|
||||||
|
'<<Cut>>': ['<Control-x>'],
|
||||||
|
'<<Paste>>': ['<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>'],
|
||||||
|
'<<python-docs>>': ['<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>'],
|
||||||
|
'<<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>>': ['<Alt-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>'],
|
||||||
|
'<<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>']}
|
||||||
64
Lib/idlelib/loader.py
Normal file
64
Lib/idlelib/loader.py
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
# Everything is done inside the loader function so that no other names
|
||||||
|
# are placed in the global namespace. Before user code is executed,
|
||||||
|
# even this name is unbound.
|
||||||
|
def loader():
|
||||||
|
import sys, os, protocol, threading, time
|
||||||
|
import Remote
|
||||||
|
|
||||||
|
## Use to debug the loading process itself:
|
||||||
|
## sys.stdout = open('c:\\windows\\desktop\\stdout.txt','a')
|
||||||
|
## sys.stderr = open('c:\\windows\\desktop\\stderr.txt','a')
|
||||||
|
|
||||||
|
# Ensure that there is absolutely no pollution of the global
|
||||||
|
# namespace by deleting the global name of this function.
|
||||||
|
global loader
|
||||||
|
del loader
|
||||||
|
|
||||||
|
# Connect to IDLE
|
||||||
|
try:
|
||||||
|
client = protocol.Client()
|
||||||
|
except protocol.connectionLost, cL:
|
||||||
|
print 'loader: Unable to connect to IDLE', cL
|
||||||
|
return
|
||||||
|
|
||||||
|
# Connect to an ExecBinding object that needs our help. If
|
||||||
|
# the user is starting multiple programs right now, we might get a
|
||||||
|
# different one than the one that started us. Proving that's okay is
|
||||||
|
# left as an exercise to the reader. (HINT: Twelve, by the pigeonhole
|
||||||
|
# principle)
|
||||||
|
ExecBinding = client.getobject('ExecBinding')
|
||||||
|
if not ExecBinding:
|
||||||
|
print "loader: IDLE does not need me."
|
||||||
|
return
|
||||||
|
|
||||||
|
# All of our input and output goes through ExecBinding.
|
||||||
|
sys.stdin = Remote.pseudoIn( ExecBinding.readline )
|
||||||
|
sys.stdout = Remote.pseudoOut( ExecBinding.write.void, tag="stdout" )
|
||||||
|
sys.stderr = Remote.pseudoOut( ExecBinding.write.void, tag="stderr" )
|
||||||
|
|
||||||
|
# Create a Remote object and start it running.
|
||||||
|
remote = Remote.Remote(globals(), ExecBinding)
|
||||||
|
rthread = threading.Thread(target=remote.mainloop)
|
||||||
|
rthread.setDaemon(1)
|
||||||
|
rthread.start()
|
||||||
|
|
||||||
|
# Block until either the client or the user program stops
|
||||||
|
user = rthread.isAlive
|
||||||
|
while user and client.isAlive():
|
||||||
|
time.sleep(0.025)
|
||||||
|
|
||||||
|
if not user():
|
||||||
|
user = hasattr(sys, "ready_to_exit") and sys.ready_to_exit
|
||||||
|
for t in threading.enumerate():
|
||||||
|
if not t.isDaemon() and t.isAlive() and t!=threading.currentThread():
|
||||||
|
user = t.isAlive
|
||||||
|
break
|
||||||
|
|
||||||
|
# We need to make sure we actually exit, so that the user doesn't get
|
||||||
|
# stuck with an invisible process. We want to finalize C modules, so
|
||||||
|
# we don't use os._exit(), but we don't call sys.exitfunc, which might
|
||||||
|
# block forever.
|
||||||
|
del sys.exitfunc
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
loader()
|
||||||
369
Lib/idlelib/protocol.py
Normal file
369
Lib/idlelib/protocol.py
Normal file
|
|
@ -0,0 +1,369 @@
|
||||||
|
"""protocol (David Scherer <dscherer@cmu.edu>)
|
||||||
|
|
||||||
|
This module implements a simple RPC or "distributed object" protocol.
|
||||||
|
I am probably the 100,000th person to write this in Python, but, hey,
|
||||||
|
it was fun.
|
||||||
|
|
||||||
|
Contents:
|
||||||
|
|
||||||
|
connectionLost is an exception that will be thrown by functions in
|
||||||
|
the protocol module or calls to remote methods that fail because
|
||||||
|
the remote program has closed the socket or because no connection
|
||||||
|
could be established in the first place.
|
||||||
|
|
||||||
|
Server( port=None, connection_hook=None ) creates a server on a
|
||||||
|
well-known port, to which clients can connect. When a client
|
||||||
|
connects, a Connection is created for it. If connection_hook
|
||||||
|
is defined, then connection_hook( socket.getpeername() ) is called
|
||||||
|
before a Connection is created, and if it returns false then the
|
||||||
|
connection is refused. connection_hook must be prepared to be
|
||||||
|
called from any thread.
|
||||||
|
|
||||||
|
Client( ip='127.0.0.1', port=None ) returns a Connection to a Server
|
||||||
|
object at a well-known address and port.
|
||||||
|
|
||||||
|
Connection( socket ) creates an RPC connection on an arbitrary socket,
|
||||||
|
which must already be connected to another program. You do not
|
||||||
|
need to use this directly if you are using Client() or Server().
|
||||||
|
|
||||||
|
publish( name, connect_function ) provides an object with the
|
||||||
|
specified name to some or all Connections. When another program
|
||||||
|
calls Connection.getobject() with the specified name, the
|
||||||
|
specified connect_function is called with the arguments
|
||||||
|
|
||||||
|
connect_function( conn, addr )
|
||||||
|
|
||||||
|
where conn is the Connection object to the requesting client and
|
||||||
|
addr is the address returned by socket.getpeername(). If that
|
||||||
|
function returns an object, that object becomes accessible to
|
||||||
|
the caller. If it returns None, the caller's request fails.
|
||||||
|
|
||||||
|
Connection objects:
|
||||||
|
|
||||||
|
.close() refuses additional RPC messages from the peer, and notifies
|
||||||
|
the peer that the connection has been closed. All pending remote
|
||||||
|
method calls in either program will fail with a connectionLost
|
||||||
|
exception. Further remote method calls on this connection will
|
||||||
|
also result in errors.
|
||||||
|
|
||||||
|
.getobject(name) returns a proxy for the remote object with the
|
||||||
|
specified name, if it exists and the peer permits us access.
|
||||||
|
Otherwise, it returns None. It may throw a connectionLost
|
||||||
|
exception. The returned proxy supports basic attribute access
|
||||||
|
and method calls, and its methods have an extra attribute,
|
||||||
|
.void, which is a function that has the same effect but always
|
||||||
|
returns None. This last capability is provided as a performance
|
||||||
|
hack: object.method.void(params) can return without waiting for
|
||||||
|
the remote process to respond, but object.method(params) needs
|
||||||
|
to wait for a return value or exception.
|
||||||
|
|
||||||
|
.rpc_loop(block=0) processes *incoming* messages for this connection.
|
||||||
|
If block=1, it continues processing until an exception or return
|
||||||
|
value is received, which is normally forever. Otherwise it
|
||||||
|
returns when all currently pending messages have been delivered.
|
||||||
|
It may throw a connectionLost exception.
|
||||||
|
|
||||||
|
.set_close_hook(f) specifies a function to be called when the remote
|
||||||
|
object closes the connection during a call to rpc_loop(). This
|
||||||
|
is a good way for servers to be notified when clients disconnect.
|
||||||
|
|
||||||
|
.set_shutdown_hook(f) specifies a function called *immediately* when
|
||||||
|
the receive loop detects that the connection has been lost. The
|
||||||
|
provided function must be prepared to run in any thread.
|
||||||
|
|
||||||
|
Server objects:
|
||||||
|
|
||||||
|
.rpc_loop() processes incoming messages on all connections, and
|
||||||
|
returns when all pending messages have been processed. It will
|
||||||
|
*not* throw connectionLost exceptions; the
|
||||||
|
Connection.set_close_hook() mechanism is much better for servers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys, os, string, types
|
||||||
|
import socket
|
||||||
|
from threading import Thread
|
||||||
|
from Queue import Queue, Empty
|
||||||
|
from cPickle import Pickler, Unpickler, PicklingError
|
||||||
|
|
||||||
|
class connectionLost:
|
||||||
|
def __init__(self, what=""): self.what = what
|
||||||
|
def __repr__(self): return self.what
|
||||||
|
def __str__(self): return self.what
|
||||||
|
|
||||||
|
def getmethods(cls):
|
||||||
|
"Returns a list of the names of the methods of a class."
|
||||||
|
methods = []
|
||||||
|
for b in cls.__bases__:
|
||||||
|
methods = methods + getmethods(b)
|
||||||
|
d = cls.__dict__
|
||||||
|
for k in d.keys():
|
||||||
|
if type(d[k])==types.FunctionType:
|
||||||
|
methods.append(k)
|
||||||
|
return methods
|
||||||
|
|
||||||
|
class methodproxy:
|
||||||
|
"Proxy for a method of a remote object."
|
||||||
|
def __init__(self, classp, name):
|
||||||
|
self.classp=classp
|
||||||
|
self.name=name
|
||||||
|
self.client = classp.client
|
||||||
|
def __call__(self, *args, **keywords):
|
||||||
|
return self.client.call( 'm', self.classp.name, self.name, args, keywords )
|
||||||
|
|
||||||
|
def void(self, *args, **keywords):
|
||||||
|
self.client.call_void( 'm', self.classp.name,self.name,args,keywords)
|
||||||
|
|
||||||
|
class classproxy:
|
||||||
|
"Proxy for a remote object."
|
||||||
|
def __init__(self, client, name, methods):
|
||||||
|
self.__dict__['client'] = client
|
||||||
|
self.__dict__['name'] = name
|
||||||
|
|
||||||
|
for m in methods:
|
||||||
|
prox = methodproxy( self, m )
|
||||||
|
self.__dict__[m] = prox
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
return self.client.call( 'g', self.name, attr )
|
||||||
|
|
||||||
|
def __setattr__(self, attr, value):
|
||||||
|
self.client.call_void( 's', self.name, attr, value )
|
||||||
|
|
||||||
|
local_connect = {}
|
||||||
|
def publish(name, connect_function):
|
||||||
|
local_connect[name]=connect_function
|
||||||
|
|
||||||
|
class socketFile:
|
||||||
|
"File emulator based on a socket. Provides only blocking semantics for now."
|
||||||
|
|
||||||
|
def __init__(self, socket):
|
||||||
|
self.socket = socket
|
||||||
|
self.buffer = ''
|
||||||
|
|
||||||
|
def _recv(self,bytes):
|
||||||
|
try:
|
||||||
|
r=self.socket.recv(bytes)
|
||||||
|
except:
|
||||||
|
raise connectionLost()
|
||||||
|
if not r:
|
||||||
|
raise connectionLost()
|
||||||
|
return r
|
||||||
|
|
||||||
|
def write(self, string):
|
||||||
|
try:
|
||||||
|
self.socket.send( string )
|
||||||
|
except:
|
||||||
|
raise connectionLost()
|
||||||
|
|
||||||
|
def read(self,bytes):
|
||||||
|
x = bytes-len(self.buffer)
|
||||||
|
while x>0:
|
||||||
|
self.buffer=self.buffer+self._recv(x)
|
||||||
|
x = bytes-len(self.buffer)
|
||||||
|
s = self.buffer[:bytes]
|
||||||
|
self.buffer=self.buffer[bytes:]
|
||||||
|
return s
|
||||||
|
|
||||||
|
def readline(self):
|
||||||
|
while 1:
|
||||||
|
f = string.find(self.buffer,'\n')
|
||||||
|
if f>=0:
|
||||||
|
s = self.buffer[:f+1]
|
||||||
|
self.buffer=self.buffer[f+1:]
|
||||||
|
return s
|
||||||
|
self.buffer = self.buffer + self._recv(1024)
|
||||||
|
|
||||||
|
|
||||||
|
class Connection (Thread):
|
||||||
|
debug = 0
|
||||||
|
def __init__(self, socket):
|
||||||
|
self.local_objects = {}
|
||||||
|
self.socket = socket
|
||||||
|
self.name = socket.getpeername()
|
||||||
|
self.socketfile = socketFile(socket)
|
||||||
|
self.queue = Queue(-1)
|
||||||
|
self.refuse_messages = 0
|
||||||
|
self.cmds = { 'm': self.r_meth,
|
||||||
|
'g': self.r_get,
|
||||||
|
's': self.r_set,
|
||||||
|
'o': self.r_geto,
|
||||||
|
'e': self.r_exc,
|
||||||
|
#'r' handled by rpc_loop
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.__init__(self)
|
||||||
|
self.setDaemon(1)
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
def getobject(self, name):
|
||||||
|
methods = self.call( 'o', name )
|
||||||
|
if methods is None: return None
|
||||||
|
return classproxy(self, name, methods)
|
||||||
|
|
||||||
|
# close_hook is called from rpc_loop(), like a normal remote method
|
||||||
|
# invocation
|
||||||
|
def set_close_hook(self,hook): self.close_hook = hook
|
||||||
|
|
||||||
|
# shutdown_hook is called directly from the run() thread, and needs
|
||||||
|
# to be "thread safe"
|
||||||
|
def set_shutdown_hook(self,hook): self.shutdown_hook = hook
|
||||||
|
|
||||||
|
close_hook = None
|
||||||
|
shutdown_hook = None
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self._shutdown()
|
||||||
|
self.refuse_messages = 1
|
||||||
|
|
||||||
|
def call(self, c, *args):
|
||||||
|
self.send( (c, args, 1 ) )
|
||||||
|
return self.rpc_loop( block = 1 )
|
||||||
|
|
||||||
|
def call_void(self, c, *args):
|
||||||
|
try:
|
||||||
|
self.send( (c, args, 0 ) )
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# the following methods handle individual RPC calls:
|
||||||
|
|
||||||
|
def r_geto(self, obj):
|
||||||
|
c = local_connect.get(obj)
|
||||||
|
if not c: return None
|
||||||
|
o = c(self, self.name)
|
||||||
|
if not o: return None
|
||||||
|
self.local_objects[obj] = o
|
||||||
|
return getmethods(o.__class__)
|
||||||
|
|
||||||
|
def r_meth(self, obj, name, args, keywords):
|
||||||
|
return apply( getattr(self.local_objects[obj],name), args, keywords)
|
||||||
|
|
||||||
|
def r_get(self, obj, name):
|
||||||
|
return getattr(self.local_objects[obj],name)
|
||||||
|
|
||||||
|
def r_set(self, obj, name, value):
|
||||||
|
setattr(self.local_objects[obj],name,value)
|
||||||
|
|
||||||
|
def r_exc(self, e, v):
|
||||||
|
raise e, v
|
||||||
|
|
||||||
|
def rpc_exec(self, cmd, arg, ret):
|
||||||
|
if self.refuse_messages: return
|
||||||
|
if self.debug: print cmd,arg,ret
|
||||||
|
if ret:
|
||||||
|
try:
|
||||||
|
r=apply(self.cmds.get(cmd), arg)
|
||||||
|
self.send( ('r', r, 0) )
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
self.send( ('e', sys.exc_info()[:2], 0) )
|
||||||
|
except PicklingError:
|
||||||
|
self.send( ('e', (TypeError, 'Unpicklable exception.'), 0 ) )
|
||||||
|
else:
|
||||||
|
# we cannot report exceptions to the caller, so
|
||||||
|
# we report them in this process.
|
||||||
|
r=apply(self.cmds.get(cmd), arg)
|
||||||
|
|
||||||
|
# the following methods implement the RPC and message loops:
|
||||||
|
|
||||||
|
def rpc_loop(self, block=0):
|
||||||
|
if self.refuse_messages: raise connectionLost('(already closed)')
|
||||||
|
try:
|
||||||
|
while 1:
|
||||||
|
try:
|
||||||
|
cmd, arg, ret = self.queue.get( block )
|
||||||
|
except Empty:
|
||||||
|
return None
|
||||||
|
if cmd=='r': return arg
|
||||||
|
self.rpc_exec(cmd,arg,ret)
|
||||||
|
except connectionLost:
|
||||||
|
if self.close_hook:
|
||||||
|
self.close_hook()
|
||||||
|
self.close_hook = None
|
||||||
|
raise
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
while 1:
|
||||||
|
data = self.recv()
|
||||||
|
self.queue.put( data )
|
||||||
|
except:
|
||||||
|
self.queue.put( ('e', sys.exc_info()[:2], 0) )
|
||||||
|
|
||||||
|
# The following send raw pickled data to the peer
|
||||||
|
|
||||||
|
def send(self, data):
|
||||||
|
try:
|
||||||
|
Pickler(self.socketfile,1).dump( data )
|
||||||
|
except connectionLost:
|
||||||
|
self._shutdown()
|
||||||
|
if self.shutdown_hook: self.shutdown_hook()
|
||||||
|
raise
|
||||||
|
|
||||||
|
def recv(self):
|
||||||
|
try:
|
||||||
|
return Unpickler(self.socketfile).load()
|
||||||
|
except connectionLost:
|
||||||
|
self._shutdown()
|
||||||
|
if self.shutdown_hook: self.shutdown_hook()
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def _shutdown(self):
|
||||||
|
try:
|
||||||
|
self.socket.shutdown(1)
|
||||||
|
self.socket.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Server (Thread):
|
||||||
|
default_port = 0x1D1E # "IDlE"
|
||||||
|
|
||||||
|
def __init__(self, port=None, connection_hook=None):
|
||||||
|
self.connections = []
|
||||||
|
self.port = port or self.default_port
|
||||||
|
self.connection_hook = connection_hook
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.wellknown = s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.bind('', self.port)
|
||||||
|
s.listen(3)
|
||||||
|
except:
|
||||||
|
raise connectionLost
|
||||||
|
|
||||||
|
Thread.__init__(self)
|
||||||
|
self.setDaemon(1)
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
s = self.wellknown
|
||||||
|
while 1:
|
||||||
|
conn, addr = s.accept()
|
||||||
|
if self.connection_hook and not self.connection_hook(addr):
|
||||||
|
try:
|
||||||
|
conn.shutdown(1)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
continue
|
||||||
|
self.connections.append( Connection(conn) )
|
||||||
|
|
||||||
|
def rpc_loop(self):
|
||||||
|
cns = self.connections[:]
|
||||||
|
for c in cns:
|
||||||
|
try:
|
||||||
|
c.rpc_loop(block = 0)
|
||||||
|
except connectionLost:
|
||||||
|
if c in self.connections:
|
||||||
|
self.connections.remove(c)
|
||||||
|
|
||||||
|
def Client(ip='127.0.0.1', port=None):
|
||||||
|
try:
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.connect(ip,port or Server.default_port)
|
||||||
|
except socket.error, what:
|
||||||
|
raise connectionLost(str(what))
|
||||||
|
except:
|
||||||
|
raise connectionLost()
|
||||||
|
return Connection(s)
|
||||||
336
Lib/idlelib/pyclbr.py
Normal file
336
Lib/idlelib/pyclbr.py
Normal file
|
|
@ -0,0 +1,336 @@
|
||||||
|
"""Parse a Python file and retrieve classes and methods.
|
||||||
|
|
||||||
|
Parse enough of a Python file to recognize class and method
|
||||||
|
definitions and to find out the superclasses of a class.
|
||||||
|
|
||||||
|
The interface consists of a single function:
|
||||||
|
readmodule(module, path)
|
||||||
|
module is the name of a Python module, path is an optional list of
|
||||||
|
directories where the module is to be searched. If present, path is
|
||||||
|
prepended to the system search path sys.path.
|
||||||
|
The return value is a dictionary. The keys of the dictionary are
|
||||||
|
the names of the classes defined in the module (including classes
|
||||||
|
that are defined via the from XXX import YYY construct). The values
|
||||||
|
are class instances of the class Class defined here.
|
||||||
|
|
||||||
|
A class is described by the class Class in this module. Instances
|
||||||
|
of this class have the following instance variables:
|
||||||
|
name -- the name of the class
|
||||||
|
super -- a list of super classes (Class instances)
|
||||||
|
methods -- a dictionary of methods
|
||||||
|
file -- the file in which the class was defined
|
||||||
|
lineno -- the line in the file on which the class statement occurred
|
||||||
|
The dictionary of methods uses the method names as keys and the line
|
||||||
|
numbers on which the method was defined as values.
|
||||||
|
If the name of a super class is not recognized, the corresponding
|
||||||
|
entry in the list of super classes is not a class instance but a
|
||||||
|
string giving the name of the super class. Since import statements
|
||||||
|
are recognized and imported modules are scanned as well, this
|
||||||
|
shouldn't happen often.
|
||||||
|
|
||||||
|
BUGS
|
||||||
|
- Continuation lines are not dealt with at all.
|
||||||
|
- While triple-quoted strings won't confuse it, lines that look like
|
||||||
|
def, class, import or "from ... import" stmts inside backslash-continued
|
||||||
|
single-quoted strings are treated like code. The expense of stopping
|
||||||
|
that isn't worth it.
|
||||||
|
- Code that doesn't pass tabnanny or python -t will confuse it, unless
|
||||||
|
you set the module TABWIDTH vrbl (default 8) to the correct tab width
|
||||||
|
for the file.
|
||||||
|
|
||||||
|
PACKAGE RELATED BUGS
|
||||||
|
- If you have a package and a module inside that or another package
|
||||||
|
with the same name, module caching doesn't work properly since the
|
||||||
|
key is the base name of the module/package.
|
||||||
|
- The only entry that is returned when you readmodule a package is a
|
||||||
|
__path__ whose value is a list which confuses certain class browsers.
|
||||||
|
- When code does:
|
||||||
|
from package import subpackage
|
||||||
|
class MyClass(subpackage.SuperClass):
|
||||||
|
...
|
||||||
|
It can't locate the parent. It probably needs to have the same
|
||||||
|
hairy logic that the import locator already does. (This logic
|
||||||
|
exists coded in Python in the freeze package.)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import imp
|
||||||
|
import re
|
||||||
|
import string
|
||||||
|
|
||||||
|
TABWIDTH = 8
|
||||||
|
|
||||||
|
_getnext = re.compile(r"""
|
||||||
|
(?P<String>
|
||||||
|
\""" [^"\\]* (?:
|
||||||
|
(?: \\. | "(?!"") )
|
||||||
|
[^"\\]*
|
||||||
|
)*
|
||||||
|
\"""
|
||||||
|
|
||||||
|
| ''' [^'\\]* (?:
|
||||||
|
(?: \\. | '(?!'') )
|
||||||
|
[^'\\]*
|
||||||
|
)*
|
||||||
|
'''
|
||||||
|
)
|
||||||
|
|
||||||
|
| (?P<Method>
|
||||||
|
^
|
||||||
|
(?P<MethodIndent> [ \t]* )
|
||||||
|
def [ \t]+
|
||||||
|
(?P<MethodName> [a-zA-Z_] \w* )
|
||||||
|
[ \t]* \(
|
||||||
|
)
|
||||||
|
|
||||||
|
| (?P<Class>
|
||||||
|
^
|
||||||
|
(?P<ClassIndent> [ \t]* )
|
||||||
|
class [ \t]+
|
||||||
|
(?P<ClassName> [a-zA-Z_] \w* )
|
||||||
|
[ \t]*
|
||||||
|
(?P<ClassSupers> \( [^)\n]* \) )?
|
||||||
|
[ \t]* :
|
||||||
|
)
|
||||||
|
|
||||||
|
| (?P<Import>
|
||||||
|
^ import [ \t]+
|
||||||
|
(?P<ImportList> [^#;\n]+ )
|
||||||
|
)
|
||||||
|
|
||||||
|
| (?P<ImportFrom>
|
||||||
|
^ from [ \t]+
|
||||||
|
(?P<ImportFromPath>
|
||||||
|
[a-zA-Z_] \w*
|
||||||
|
(?:
|
||||||
|
[ \t]* \. [ \t]* [a-zA-Z_] \w*
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
[ \t]+
|
||||||
|
import [ \t]+
|
||||||
|
(?P<ImportFromList> [^#;\n]+ )
|
||||||
|
)
|
||||||
|
""", re.VERBOSE | re.DOTALL | re.MULTILINE).search
|
||||||
|
|
||||||
|
_modules = {} # cache of modules we've seen
|
||||||
|
|
||||||
|
# each Python class is represented by an instance of this class
|
||||||
|
class Class:
|
||||||
|
'''Class to represent a Python class.'''
|
||||||
|
def __init__(self, module, name, super, file, lineno):
|
||||||
|
self.module = module
|
||||||
|
self.name = name
|
||||||
|
if super is None:
|
||||||
|
super = []
|
||||||
|
self.super = super
|
||||||
|
self.methods = {}
|
||||||
|
self.file = file
|
||||||
|
self.lineno = lineno
|
||||||
|
|
||||||
|
def _addmethod(self, name, lineno):
|
||||||
|
self.methods[name] = lineno
|
||||||
|
|
||||||
|
class Function(Class):
|
||||||
|
'''Class to represent a top-level Python function'''
|
||||||
|
def __init__(self, module, name, file, lineno):
|
||||||
|
Class.__init__(self, module, name, None, file, lineno)
|
||||||
|
def _addmethod(self, name, lineno):
|
||||||
|
assert 0, "Function._addmethod() shouldn't be called"
|
||||||
|
|
||||||
|
def readmodule(module, path=[], inpackage=0):
|
||||||
|
'''Backwards compatible interface.
|
||||||
|
|
||||||
|
Like readmodule_ex() but strips Function objects from the
|
||||||
|
resulting dictionary.'''
|
||||||
|
|
||||||
|
dict = readmodule_ex(module, path, inpackage)
|
||||||
|
res = {}
|
||||||
|
for key, value in dict.items():
|
||||||
|
if not isinstance(value, Function):
|
||||||
|
res[key] = value
|
||||||
|
return res
|
||||||
|
|
||||||
|
def readmodule_ex(module, path=[], inpackage=0):
|
||||||
|
'''Read a module file and return a dictionary of classes.
|
||||||
|
|
||||||
|
Search for MODULE in PATH and sys.path, read and parse the
|
||||||
|
module and return a dictionary with one entry for each class
|
||||||
|
found in the module.'''
|
||||||
|
|
||||||
|
dict = {}
|
||||||
|
|
||||||
|
i = string.rfind(module, '.')
|
||||||
|
if i >= 0:
|
||||||
|
# Dotted module name
|
||||||
|
package = string.strip(module[:i])
|
||||||
|
submodule = string.strip(module[i+1:])
|
||||||
|
parent = readmodule(package, path, inpackage)
|
||||||
|
child = readmodule(submodule, parent['__path__'], 1)
|
||||||
|
return child
|
||||||
|
|
||||||
|
if _modules.has_key(module):
|
||||||
|
# we've seen this module before...
|
||||||
|
return _modules[module]
|
||||||
|
if module in sys.builtin_module_names:
|
||||||
|
# this is a built-in module
|
||||||
|
_modules[module] = dict
|
||||||
|
return dict
|
||||||
|
|
||||||
|
# search the path for the module
|
||||||
|
f = None
|
||||||
|
if inpackage:
|
||||||
|
try:
|
||||||
|
f, file, (suff, mode, type) = \
|
||||||
|
imp.find_module(module, path)
|
||||||
|
except ImportError:
|
||||||
|
f = None
|
||||||
|
if f is None:
|
||||||
|
fullpath = list(path) + sys.path
|
||||||
|
f, file, (suff, mode, type) = imp.find_module(module, fullpath)
|
||||||
|
if type == imp.PKG_DIRECTORY:
|
||||||
|
dict['__path__'] = [file]
|
||||||
|
_modules[module] = dict
|
||||||
|
path = [file] + path
|
||||||
|
f, file, (suff, mode, type) = \
|
||||||
|
imp.find_module('__init__', [file])
|
||||||
|
if type != imp.PY_SOURCE:
|
||||||
|
# not Python source, can't do anything with this module
|
||||||
|
f.close()
|
||||||
|
_modules[module] = dict
|
||||||
|
return dict
|
||||||
|
|
||||||
|
_modules[module] = dict
|
||||||
|
imports = []
|
||||||
|
classstack = [] # stack of (class, indent) pairs
|
||||||
|
src = f.read()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
# To avoid having to stop the regexp at each newline, instead
|
||||||
|
# when we need a line number we simply string.count the number of
|
||||||
|
# newlines in the string since the last time we did this; i.e.,
|
||||||
|
# lineno = lineno + \
|
||||||
|
# string.count(src, '\n', last_lineno_pos, here)
|
||||||
|
# last_lineno_pos = here
|
||||||
|
countnl = string.count
|
||||||
|
lineno, last_lineno_pos = 1, 0
|
||||||
|
i = 0
|
||||||
|
while 1:
|
||||||
|
m = _getnext(src, i)
|
||||||
|
if not m:
|
||||||
|
break
|
||||||
|
start, i = m.span()
|
||||||
|
|
||||||
|
if m.start("Method") >= 0:
|
||||||
|
# found a method definition or function
|
||||||
|
thisindent = _indent(m.group("MethodIndent"))
|
||||||
|
meth_name = m.group("MethodName")
|
||||||
|
lineno = lineno + \
|
||||||
|
countnl(src, '\n',
|
||||||
|
last_lineno_pos, start)
|
||||||
|
last_lineno_pos = start
|
||||||
|
# close all classes indented at least as much
|
||||||
|
while classstack and \
|
||||||
|
classstack[-1][1] >= thisindent:
|
||||||
|
del classstack[-1]
|
||||||
|
if classstack:
|
||||||
|
# it's a class method
|
||||||
|
cur_class = classstack[-1][0]
|
||||||
|
cur_class._addmethod(meth_name, lineno)
|
||||||
|
else:
|
||||||
|
# it's a function
|
||||||
|
f = Function(module, meth_name,
|
||||||
|
file, lineno)
|
||||||
|
dict[meth_name] = f
|
||||||
|
|
||||||
|
elif m.start("String") >= 0:
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif m.start("Class") >= 0:
|
||||||
|
# we found a class definition
|
||||||
|
thisindent = _indent(m.group("ClassIndent"))
|
||||||
|
# close all classes indented at least as much
|
||||||
|
while classstack and \
|
||||||
|
classstack[-1][1] >= thisindent:
|
||||||
|
del classstack[-1]
|
||||||
|
lineno = lineno + \
|
||||||
|
countnl(src, '\n', last_lineno_pos, start)
|
||||||
|
last_lineno_pos = start
|
||||||
|
class_name = m.group("ClassName")
|
||||||
|
inherit = m.group("ClassSupers")
|
||||||
|
if inherit:
|
||||||
|
# the class inherits from other classes
|
||||||
|
inherit = string.strip(inherit[1:-1])
|
||||||
|
names = []
|
||||||
|
for n in string.splitfields(inherit, ','):
|
||||||
|
n = string.strip(n)
|
||||||
|
if dict.has_key(n):
|
||||||
|
# we know this super class
|
||||||
|
n = dict[n]
|
||||||
|
else:
|
||||||
|
c = string.splitfields(n, '.')
|
||||||
|
if len(c) > 1:
|
||||||
|
# super class
|
||||||
|
# is of the
|
||||||
|
# form module.class:
|
||||||
|
# look in
|
||||||
|
# module for class
|
||||||
|
m = c[-2]
|
||||||
|
c = c[-1]
|
||||||
|
if _modules.has_key(m):
|
||||||
|
d = _modules[m]
|
||||||
|
if d.has_key(c):
|
||||||
|
n = d[c]
|
||||||
|
names.append(n)
|
||||||
|
inherit = names
|
||||||
|
# remember this class
|
||||||
|
cur_class = Class(module, class_name, inherit,
|
||||||
|
file, lineno)
|
||||||
|
dict[class_name] = cur_class
|
||||||
|
classstack.append((cur_class, thisindent))
|
||||||
|
|
||||||
|
elif m.start("Import") >= 0:
|
||||||
|
# import module
|
||||||
|
for n in string.split(m.group("ImportList"), ','):
|
||||||
|
n = string.strip(n)
|
||||||
|
try:
|
||||||
|
# recursively read the imported module
|
||||||
|
d = readmodule(n, path, inpackage)
|
||||||
|
except:
|
||||||
|
##print 'module', n, 'not found'
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif m.start("ImportFrom") >= 0:
|
||||||
|
# from module import stuff
|
||||||
|
mod = m.group("ImportFromPath")
|
||||||
|
names = string.split(m.group("ImportFromList"), ',')
|
||||||
|
try:
|
||||||
|
# recursively read the imported module
|
||||||
|
d = readmodule(mod, path, inpackage)
|
||||||
|
except:
|
||||||
|
##print 'module', mod, 'not found'
|
||||||
|
continue
|
||||||
|
# add any classes that were defined in the
|
||||||
|
# imported module to our name space if they
|
||||||
|
# were mentioned in the list
|
||||||
|
for n in names:
|
||||||
|
n = string.strip(n)
|
||||||
|
if d.has_key(n):
|
||||||
|
dict[n] = d[n]
|
||||||
|
elif n == '*':
|
||||||
|
# only add a name if not
|
||||||
|
# already there (to mimic what
|
||||||
|
# Python does internally)
|
||||||
|
# also don't add names that
|
||||||
|
# start with _
|
||||||
|
for n in d.keys():
|
||||||
|
if n[0] != '_' and \
|
||||||
|
not dict.has_key(n):
|
||||||
|
dict[n] = d[n]
|
||||||
|
else:
|
||||||
|
assert 0, "regexp _getnext found something unexpected"
|
||||||
|
|
||||||
|
return dict
|
||||||
|
|
||||||
|
def _indent(ws, _expandtabs=string.expandtabs):
|
||||||
|
return len(_expandtabs(ws, TABWIDTH))
|
||||||
59
Lib/idlelib/spawn.py
Normal file
59
Lib/idlelib/spawn.py
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
# spawn - This is ugly, OS-specific code to spawn a separate process. It
|
||||||
|
# also defines a function for getting the version of a path most
|
||||||
|
# likely to work with cranky API functions.
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
def hardpath(path):
|
||||||
|
path = os.path.normcase(os.path.abspath(path))
|
||||||
|
try:
|
||||||
|
import win32api
|
||||||
|
path = win32api.GetShortPathName( path )
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return path
|
||||||
|
|
||||||
|
if hasattr(os, 'spawnv'):
|
||||||
|
|
||||||
|
# Windows-ish OS: we use spawnv(), and stick quotes around arguments
|
||||||
|
# in case they contains spaces, since Windows will jam all the
|
||||||
|
# arguments to spawn() or exec() together into one string. The
|
||||||
|
# kill_zombies function is a noop.
|
||||||
|
|
||||||
|
def spawn(bin, *args):
|
||||||
|
nargs = [bin]
|
||||||
|
for arg in args:
|
||||||
|
nargs.append( '"'+arg+'"' )
|
||||||
|
os.spawnv( os.P_NOWAIT, bin, nargs )
|
||||||
|
|
||||||
|
def kill_zombies(): pass
|
||||||
|
|
||||||
|
elif hasattr(os, 'fork'):
|
||||||
|
|
||||||
|
# UNIX-ish operating system: we fork() and exec(), and we have to track
|
||||||
|
# the pids of our children and call waitpid() on them to avoid leaving
|
||||||
|
# zombies in the process table. kill_zombies() does the dirty work, and
|
||||||
|
# should be called periodically.
|
||||||
|
|
||||||
|
zombies = []
|
||||||
|
|
||||||
|
def spawn(bin, *args):
|
||||||
|
pid = os.fork()
|
||||||
|
if pid:
|
||||||
|
zombies.append(pid)
|
||||||
|
else:
|
||||||
|
os.execv( bin, (bin, ) + args )
|
||||||
|
|
||||||
|
def kill_zombies():
|
||||||
|
for z in zombies[:]:
|
||||||
|
stat = os.waitpid(z, os.WNOHANG)
|
||||||
|
if stat[0]==z:
|
||||||
|
zombies.remove(z)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# If you get here, you may be able to write an alternative implementation
|
||||||
|
# of these functions for your OS.
|
||||||
|
|
||||||
|
def kill_zombies(): pass
|
||||||
|
|
||||||
|
raise OSError, 'This OS does not support fork() or spawnv().'
|
||||||
372
Lib/idlelib/tabnanny.py
Normal file
372
Lib/idlelib/tabnanny.py
Normal file
|
|
@ -0,0 +1,372 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
|
||||||
|
"""The Tab Nanny despises ambiguous indentation. She knows no mercy."""
|
||||||
|
|
||||||
|
# Released to the public domain, by Tim Peters, 15 April 1998.
|
||||||
|
|
||||||
|
# XXX Note: this is now a standard library module.
|
||||||
|
# XXX The API needs to undergo changes however; the current code is too
|
||||||
|
# XXX script-like. This will be addressed later.
|
||||||
|
|
||||||
|
__version__ = "6"
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import string
|
||||||
|
import getopt
|
||||||
|
import tokenize
|
||||||
|
|
||||||
|
verbose = 0
|
||||||
|
filename_only = 0
|
||||||
|
|
||||||
|
def errprint(*args):
|
||||||
|
sep = ""
|
||||||
|
for arg in args:
|
||||||
|
sys.stderr.write(sep + str(arg))
|
||||||
|
sep = " "
|
||||||
|
sys.stderr.write("\n")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
global verbose, filename_only
|
||||||
|
try:
|
||||||
|
opts, args = getopt.getopt(sys.argv[1:], "qv")
|
||||||
|
except getopt.error, msg:
|
||||||
|
errprint(msg)
|
||||||
|
return
|
||||||
|
for o, a in opts:
|
||||||
|
if o == '-q':
|
||||||
|
filename_only = filename_only + 1
|
||||||
|
if o == '-v':
|
||||||
|
verbose = verbose + 1
|
||||||
|
if not args:
|
||||||
|
errprint("Usage:", sys.argv[0], "[-v] file_or_directory ...")
|
||||||
|
return
|
||||||
|
for arg in args:
|
||||||
|
check(arg)
|
||||||
|
|
||||||
|
class NannyNag:
|
||||||
|
def __init__(self, lineno, msg, line):
|
||||||
|
self.lineno, self.msg, self.line = lineno, msg, line
|
||||||
|
def get_lineno(self):
|
||||||
|
return self.lineno
|
||||||
|
def get_msg(self):
|
||||||
|
return self.msg
|
||||||
|
def get_line(self):
|
||||||
|
return self.line
|
||||||
|
|
||||||
|
def check(file):
|
||||||
|
if os.path.isdir(file) and not os.path.islink(file):
|
||||||
|
if verbose:
|
||||||
|
print "%s: listing directory" % `file`
|
||||||
|
names = os.listdir(file)
|
||||||
|
for name in names:
|
||||||
|
fullname = os.path.join(file, name)
|
||||||
|
if (os.path.isdir(fullname) and
|
||||||
|
not os.path.islink(fullname) or
|
||||||
|
os.path.normcase(name[-3:]) == ".py"):
|
||||||
|
check(fullname)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
f = open(file)
|
||||||
|
except IOError, msg:
|
||||||
|
errprint("%s: I/O Error: %s" % (`file`, str(msg)))
|
||||||
|
return
|
||||||
|
|
||||||
|
if verbose > 1:
|
||||||
|
print "checking", `file`, "..."
|
||||||
|
|
||||||
|
reset_globals()
|
||||||
|
try:
|
||||||
|
tokenize.tokenize(f.readline, tokeneater)
|
||||||
|
|
||||||
|
except tokenize.TokenError, msg:
|
||||||
|
errprint("%s: Token Error: %s" % (`file`, str(msg)))
|
||||||
|
return
|
||||||
|
|
||||||
|
except NannyNag, nag:
|
||||||
|
badline = nag.get_lineno()
|
||||||
|
line = nag.get_line()
|
||||||
|
if verbose:
|
||||||
|
print "%s: *** Line %d: trouble in tab city! ***" % (
|
||||||
|
`file`, badline)
|
||||||
|
print "offending line:", `line`
|
||||||
|
print nag.get_msg()
|
||||||
|
else:
|
||||||
|
if ' ' in file: file = '"' + file + '"'
|
||||||
|
if filename_only: print file
|
||||||
|
else: print file, badline, `line`
|
||||||
|
return
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print "%s: Clean bill of health." % `file`
|
||||||
|
|
||||||
|
class Whitespace:
|
||||||
|
# the characters used for space and tab
|
||||||
|
S, T = ' \t'
|
||||||
|
|
||||||
|
# members:
|
||||||
|
# raw
|
||||||
|
# the original string
|
||||||
|
# n
|
||||||
|
# the number of leading whitespace characters in raw
|
||||||
|
# nt
|
||||||
|
# the number of tabs in raw[:n]
|
||||||
|
# norm
|
||||||
|
# the normal form as a pair (count, trailing), where:
|
||||||
|
# count
|
||||||
|
# a tuple such that raw[:n] contains count[i]
|
||||||
|
# instances of S * i + T
|
||||||
|
# trailing
|
||||||
|
# the number of trailing spaces in raw[:n]
|
||||||
|
# It's A Theorem that m.indent_level(t) ==
|
||||||
|
# n.indent_level(t) for all t >= 1 iff m.norm == n.norm.
|
||||||
|
# is_simple
|
||||||
|
# true iff raw[:n] is of the form (T*)(S*)
|
||||||
|
|
||||||
|
def __init__(self, ws):
|
||||||
|
self.raw = ws
|
||||||
|
S, T = Whitespace.S, Whitespace.T
|
||||||
|
count = []
|
||||||
|
b = n = nt = 0
|
||||||
|
for ch in self.raw:
|
||||||
|
if ch == S:
|
||||||
|
n = n + 1
|
||||||
|
b = b + 1
|
||||||
|
elif ch == T:
|
||||||
|
n = n + 1
|
||||||
|
nt = nt + 1
|
||||||
|
if b >= len(count):
|
||||||
|
count = count + [0] * (b - len(count) + 1)
|
||||||
|
count[b] = count[b] + 1
|
||||||
|
b = 0
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
self.n = n
|
||||||
|
self.nt = nt
|
||||||
|
self.norm = tuple(count), b
|
||||||
|
self.is_simple = len(count) <= 1
|
||||||
|
|
||||||
|
# return length of longest contiguous run of spaces (whether or not
|
||||||
|
# preceding a tab)
|
||||||
|
def longest_run_of_spaces(self):
|
||||||
|
count, trailing = self.norm
|
||||||
|
return max(len(count)-1, trailing)
|
||||||
|
|
||||||
|
def indent_level(self, tabsize):
|
||||||
|
# count, il = self.norm
|
||||||
|
# for i in range(len(count)):
|
||||||
|
# if count[i]:
|
||||||
|
# il = il + (i/tabsize + 1)*tabsize * count[i]
|
||||||
|
# return il
|
||||||
|
|
||||||
|
# quicker:
|
||||||
|
# il = trailing + sum (i/ts + 1)*ts*count[i] =
|
||||||
|
# trailing + ts * sum (i/ts + 1)*count[i] =
|
||||||
|
# trailing + ts * sum i/ts*count[i] + count[i] =
|
||||||
|
# trailing + ts * [(sum i/ts*count[i]) + (sum count[i])] =
|
||||||
|
# trailing + ts * [(sum i/ts*count[i]) + num_tabs]
|
||||||
|
# and note that i/ts*count[i] is 0 when i < ts
|
||||||
|
|
||||||
|
count, trailing = self.norm
|
||||||
|
il = 0
|
||||||
|
for i in range(tabsize, len(count)):
|
||||||
|
il = il + i/tabsize * count[i]
|
||||||
|
return trailing + tabsize * (il + self.nt)
|
||||||
|
|
||||||
|
# return true iff self.indent_level(t) == other.indent_level(t)
|
||||||
|
# for all t >= 1
|
||||||
|
def equal(self, other):
|
||||||
|
return self.norm == other.norm
|
||||||
|
|
||||||
|
# return a list of tuples (ts, i1, i2) such that
|
||||||
|
# i1 == self.indent_level(ts) != other.indent_level(ts) == i2.
|
||||||
|
# Intended to be used after not self.equal(other) is known, in which
|
||||||
|
# case it will return at least one witnessing tab size.
|
||||||
|
def not_equal_witness(self, other):
|
||||||
|
n = max(self.longest_run_of_spaces(),
|
||||||
|
other.longest_run_of_spaces()) + 1
|
||||||
|
a = []
|
||||||
|
for ts in range(1, n+1):
|
||||||
|
if self.indent_level(ts) != other.indent_level(ts):
|
||||||
|
a.append( (ts,
|
||||||
|
self.indent_level(ts),
|
||||||
|
other.indent_level(ts)) )
|
||||||
|
return a
|
||||||
|
|
||||||
|
# Return true iff self.indent_level(t) < other.indent_level(t)
|
||||||
|
# for all t >= 1.
|
||||||
|
# The algorithm is due to Vincent Broman.
|
||||||
|
# Easy to prove it's correct.
|
||||||
|
# XXXpost that.
|
||||||
|
# Trivial to prove n is sharp (consider T vs ST).
|
||||||
|
# Unknown whether there's a faster general way. I suspected so at
|
||||||
|
# first, but no longer.
|
||||||
|
# For the special (but common!) case where M and N are both of the
|
||||||
|
# form (T*)(S*), M.less(N) iff M.len() < N.len() and
|
||||||
|
# M.num_tabs() <= N.num_tabs(). Proof is easy but kinda long-winded.
|
||||||
|
# XXXwrite that up.
|
||||||
|
# Note that M is of the form (T*)(S*) iff len(M.norm[0]) <= 1.
|
||||||
|
def less(self, other):
|
||||||
|
if self.n >= other.n:
|
||||||
|
return 0
|
||||||
|
if self.is_simple and other.is_simple:
|
||||||
|
return self.nt <= other.nt
|
||||||
|
n = max(self.longest_run_of_spaces(),
|
||||||
|
other.longest_run_of_spaces()) + 1
|
||||||
|
# the self.n >= other.n test already did it for ts=1
|
||||||
|
for ts in range(2, n+1):
|
||||||
|
if self.indent_level(ts) >= other.indent_level(ts):
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# return a list of tuples (ts, i1, i2) such that
|
||||||
|
# i1 == self.indent_level(ts) >= other.indent_level(ts) == i2.
|
||||||
|
# Intended to be used after not self.less(other) is known, in which
|
||||||
|
# case it will return at least one witnessing tab size.
|
||||||
|
def not_less_witness(self, other):
|
||||||
|
n = max(self.longest_run_of_spaces(),
|
||||||
|
other.longest_run_of_spaces()) + 1
|
||||||
|
a = []
|
||||||
|
for ts in range(1, n+1):
|
||||||
|
if self.indent_level(ts) >= other.indent_level(ts):
|
||||||
|
a.append( (ts,
|
||||||
|
self.indent_level(ts),
|
||||||
|
other.indent_level(ts)) )
|
||||||
|
return a
|
||||||
|
|
||||||
|
def format_witnesses(w):
|
||||||
|
import string
|
||||||
|
firsts = map(lambda tup: str(tup[0]), w)
|
||||||
|
prefix = "at tab size"
|
||||||
|
if len(w) > 1:
|
||||||
|
prefix = prefix + "s"
|
||||||
|
return prefix + " " + string.join(firsts, ', ')
|
||||||
|
|
||||||
|
# The collection of globals, the reset_globals() function, and the
|
||||||
|
# tokeneater() function, depend on which version of tokenize is
|
||||||
|
# in use.
|
||||||
|
|
||||||
|
if hasattr(tokenize, 'NL'):
|
||||||
|
# take advantage of Guido's patch!
|
||||||
|
|
||||||
|
indents = []
|
||||||
|
check_equal = 0
|
||||||
|
|
||||||
|
def reset_globals():
|
||||||
|
global indents, check_equal
|
||||||
|
check_equal = 0
|
||||||
|
indents = [Whitespace("")]
|
||||||
|
|
||||||
|
def tokeneater(type, token, start, end, line,
|
||||||
|
INDENT=tokenize.INDENT,
|
||||||
|
DEDENT=tokenize.DEDENT,
|
||||||
|
NEWLINE=tokenize.NEWLINE,
|
||||||
|
JUNK=(tokenize.COMMENT, tokenize.NL) ):
|
||||||
|
global indents, check_equal
|
||||||
|
|
||||||
|
if type == NEWLINE:
|
||||||
|
# a program statement, or ENDMARKER, will eventually follow,
|
||||||
|
# after some (possibly empty) run of tokens of the form
|
||||||
|
# (NL | COMMENT)* (INDENT | DEDENT+)?
|
||||||
|
# If an INDENT appears, setting check_equal is wrong, and will
|
||||||
|
# be undone when we see the INDENT.
|
||||||
|
check_equal = 1
|
||||||
|
|
||||||
|
elif type == INDENT:
|
||||||
|
check_equal = 0
|
||||||
|
thisguy = Whitespace(token)
|
||||||
|
if not indents[-1].less(thisguy):
|
||||||
|
witness = indents[-1].not_less_witness(thisguy)
|
||||||
|
msg = "indent not greater e.g. " + format_witnesses(witness)
|
||||||
|
raise NannyNag(start[0], msg, line)
|
||||||
|
indents.append(thisguy)
|
||||||
|
|
||||||
|
elif type == DEDENT:
|
||||||
|
# there's nothing we need to check here! what's important is
|
||||||
|
# that when the run of DEDENTs ends, the indentation of the
|
||||||
|
# program statement (or ENDMARKER) that triggered the run is
|
||||||
|
# equal to what's left at the top of the indents stack
|
||||||
|
|
||||||
|
# Ouch! This assert triggers if the last line of the source
|
||||||
|
# is indented *and* lacks a newline -- then DEDENTs pop out
|
||||||
|
# of thin air.
|
||||||
|
# assert check_equal # else no earlier NEWLINE, or an earlier INDENT
|
||||||
|
check_equal = 1
|
||||||
|
|
||||||
|
del indents[-1]
|
||||||
|
|
||||||
|
elif check_equal and type not in JUNK:
|
||||||
|
# this is the first "real token" following a NEWLINE, so it
|
||||||
|
# must be the first token of the next program statement, or an
|
||||||
|
# ENDMARKER; the "line" argument exposes the leading whitespace
|
||||||
|
# for this statement; in the case of ENDMARKER, line is an empty
|
||||||
|
# string, so will properly match the empty string with which the
|
||||||
|
# "indents" stack was seeded
|
||||||
|
check_equal = 0
|
||||||
|
thisguy = Whitespace(line)
|
||||||
|
if not indents[-1].equal(thisguy):
|
||||||
|
witness = indents[-1].not_equal_witness(thisguy)
|
||||||
|
msg = "indent not equal e.g. " + format_witnesses(witness)
|
||||||
|
raise NannyNag(start[0], msg, line)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# unpatched version of tokenize
|
||||||
|
|
||||||
|
nesting_level = 0
|
||||||
|
indents = []
|
||||||
|
check_equal = 0
|
||||||
|
|
||||||
|
def reset_globals():
|
||||||
|
global nesting_level, indents, check_equal
|
||||||
|
nesting_level = check_equal = 0
|
||||||
|
indents = [Whitespace("")]
|
||||||
|
|
||||||
|
def tokeneater(type, token, start, end, line,
|
||||||
|
INDENT=tokenize.INDENT,
|
||||||
|
DEDENT=tokenize.DEDENT,
|
||||||
|
NEWLINE=tokenize.NEWLINE,
|
||||||
|
COMMENT=tokenize.COMMENT,
|
||||||
|
OP=tokenize.OP):
|
||||||
|
global nesting_level, indents, check_equal
|
||||||
|
|
||||||
|
if type == INDENT:
|
||||||
|
check_equal = 0
|
||||||
|
thisguy = Whitespace(token)
|
||||||
|
if not indents[-1].less(thisguy):
|
||||||
|
witness = indents[-1].not_less_witness(thisguy)
|
||||||
|
msg = "indent not greater e.g. " + format_witnesses(witness)
|
||||||
|
raise NannyNag(start[0], msg, line)
|
||||||
|
indents.append(thisguy)
|
||||||
|
|
||||||
|
elif type == DEDENT:
|
||||||
|
del indents[-1]
|
||||||
|
|
||||||
|
elif type == NEWLINE:
|
||||||
|
if nesting_level == 0:
|
||||||
|
check_equal = 1
|
||||||
|
|
||||||
|
elif type == COMMENT:
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif check_equal:
|
||||||
|
check_equal = 0
|
||||||
|
thisguy = Whitespace(line)
|
||||||
|
if not indents[-1].equal(thisguy):
|
||||||
|
witness = indents[-1].not_equal_witness(thisguy)
|
||||||
|
msg = "indent not equal e.g. " + format_witnesses(witness)
|
||||||
|
raise NannyNag(start[0], msg, line)
|
||||||
|
|
||||||
|
if type == OP and token in ('{', '[', '('):
|
||||||
|
nesting_level = nesting_level + 1
|
||||||
|
|
||||||
|
elif type == OP and token in ('}', ']', ')'):
|
||||||
|
if nesting_level == 0:
|
||||||
|
raise NannyNag(start[0],
|
||||||
|
"unbalanced bracket '" + token + "'",
|
||||||
|
line)
|
||||||
|
nesting_level = nesting_level - 1
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
||||||
31
Lib/idlelib/testcode.py
Normal file
31
Lib/idlelib/testcode.py
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
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()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue