Get rid of old IDLE. Lib/idlelib rules!
This commit is contained in:
parent
91012fe9b5
commit
ecb544e95b
|
@ -24,8 +24,6 @@ i18n Tools for internationalization. pygettext.py
|
|||
and msgfmt.py generates a binary message catalog
|
||||
from a catalog in text format.
|
||||
|
||||
idle A Tkinter-based Python IDE.
|
||||
|
||||
modulator Interactively generate boiler plate for an extension
|
||||
module. Works easiest if you have Tk.
|
||||
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
import string
|
||||
import re
|
||||
|
||||
###$ event <<expand-word>>
|
||||
###$ win <Alt-slash>
|
||||
###$ unix <Alt-slash>
|
||||
|
||||
class AutoExpand:
|
||||
|
||||
keydefs = {
|
||||
'<<expand-word>>': ['<Alt-slash>'],
|
||||
}
|
||||
|
||||
unix_keydefs = {
|
||||
'<<expand-word>>': ['<Meta-slash>', '<Alt-slash>'],
|
||||
}
|
||||
|
||||
menudefs = [
|
||||
('edit', [
|
||||
('E_xpand word', '<<expand-word>>'),
|
||||
]),
|
||||
]
|
||||
|
||||
wordchars = string.ascii_letters + string.digits + "_"
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.text = editwin.text
|
||||
self.state = None
|
||||
|
||||
def expand_word_event(self, event):
|
||||
curinsert = self.text.index("insert")
|
||||
curline = self.text.get("insert linestart", "insert lineend")
|
||||
if not self.state:
|
||||
words = self.getwords()
|
||||
index = 0
|
||||
else:
|
||||
words, index, insert, line = self.state
|
||||
if insert != curinsert or line != curline:
|
||||
words = self.getwords()
|
||||
index = 0
|
||||
if not words:
|
||||
self.text.bell()
|
||||
return "break"
|
||||
word = self.getprevword()
|
||||
self.text.delete("insert - %d chars" % len(word), "insert")
|
||||
newword = words[index]
|
||||
index = (index + 1) % len(words)
|
||||
if index == 0:
|
||||
self.text.bell() # Warn we cycled around
|
||||
self.text.insert("insert", newword)
|
||||
curinsert = self.text.index("insert")
|
||||
curline = self.text.get("insert linestart", "insert lineend")
|
||||
self.state = words, index, curinsert, curline
|
||||
return "break"
|
||||
|
||||
def getwords(self):
|
||||
word = self.getprevword()
|
||||
if not word:
|
||||
return []
|
||||
before = self.text.get("1.0", "insert wordstart")
|
||||
wbefore = re.findall(r"\b" + word + r"\w+\b", before)
|
||||
del before
|
||||
after = self.text.get("insert wordend", "end")
|
||||
wafter = re.findall(r"\b" + word + r"\w+\b", after)
|
||||
del after
|
||||
if not wbefore and not wafter:
|
||||
return []
|
||||
words = []
|
||||
dict = {}
|
||||
# search backwards through words before
|
||||
wbefore.reverse()
|
||||
for w in wbefore:
|
||||
if dict.get(w):
|
||||
continue
|
||||
words.append(w)
|
||||
dict[w] = w
|
||||
# search onwards through words after
|
||||
for w in wafter:
|
||||
if dict.get(w):
|
||||
continue
|
||||
words.append(w)
|
||||
dict[w] = w
|
||||
words.append(word)
|
||||
return words
|
||||
|
||||
def getprevword(self):
|
||||
line = self.text.get("insert linestart", "insert")
|
||||
i = len(line)
|
||||
while i > 0 and line[i-1] in self.wordchars:
|
||||
i = i-1
|
||||
return line[i:]
|
|
@ -1,551 +0,0 @@
|
|||
#from Tkinter import TclError
|
||||
#import tkMessageBox
|
||||
#import tkSimpleDialog
|
||||
|
||||
###$ event <<newline-and-indent>>
|
||||
###$ win <Key-Return>
|
||||
###$ win <KP_Enter>
|
||||
###$ unix <Key-Return>
|
||||
###$ unix <KP_Enter>
|
||||
|
||||
###$ event <<indent-region>>
|
||||
###$ win <Control-bracketright>
|
||||
###$ unix <Alt-bracketright>
|
||||
###$ unix <Control-bracketright>
|
||||
|
||||
###$ event <<dedent-region>>
|
||||
###$ win <Control-bracketleft>
|
||||
###$ unix <Alt-bracketleft>
|
||||
###$ unix <Control-bracketleft>
|
||||
|
||||
###$ event <<comment-region>>
|
||||
###$ win <Alt-Key-3>
|
||||
###$ unix <Alt-Key-3>
|
||||
|
||||
###$ event <<uncomment-region>>
|
||||
###$ win <Alt-Key-4>
|
||||
###$ unix <Alt-Key-4>
|
||||
|
||||
###$ event <<tabify-region>>
|
||||
###$ win <Alt-Key-5>
|
||||
###$ unix <Alt-Key-5>
|
||||
|
||||
###$ event <<untabify-region>>
|
||||
###$ win <Alt-Key-6>
|
||||
###$ unix <Alt-Key-6>
|
||||
|
||||
import PyParse
|
||||
|
||||
class AutoIndent:
|
||||
|
||||
menudefs = [
|
||||
('edit', [
|
||||
None,
|
||||
('_Indent region', '<<indent-region>>'),
|
||||
('_Dedent region', '<<dedent-region>>'),
|
||||
('Comment _out region', '<<comment-region>>'),
|
||||
('U_ncomment region', '<<uncomment-region>>'),
|
||||
('Tabify region', '<<tabify-region>>'),
|
||||
('Untabify region', '<<untabify-region>>'),
|
||||
('Toggle tabs', '<<toggle-tabs>>'),
|
||||
('New indent width', '<<change-indentwidth>>'),
|
||||
]),
|
||||
]
|
||||
|
||||
keydefs = {
|
||||
'<<smart-backspace>>': ['<Key-BackSpace>'],
|
||||
'<<newline-and-indent>>': ['<Key-Return>', '<KP_Enter>'],
|
||||
'<<smart-indent>>': ['<Key-Tab>']
|
||||
}
|
||||
|
||||
windows_keydefs = {
|
||||
'<<indent-region>>': ['<Control-bracketright>'],
|
||||
'<<dedent-region>>': ['<Control-bracketleft>'],
|
||||
'<<comment-region>>': ['<Alt-Key-3>'],
|
||||
'<<uncomment-region>>': ['<Alt-Key-4>'],
|
||||
'<<tabify-region>>': ['<Alt-Key-5>'],
|
||||
'<<untabify-region>>': ['<Alt-Key-6>'],
|
||||
'<<toggle-tabs>>': ['<Alt-Key-t>'],
|
||||
'<<change-indentwidth>>': ['<Alt-Key-u>'],
|
||||
}
|
||||
|
||||
unix_keydefs = {
|
||||
'<<indent-region>>': ['<Alt-bracketright>',
|
||||
'<Meta-bracketright>',
|
||||
'<Control-bracketright>'],
|
||||
'<<dedent-region>>': ['<Alt-bracketleft>',
|
||||
'<Meta-bracketleft>',
|
||||
'<Control-bracketleft>'],
|
||||
'<<comment-region>>': ['<Alt-Key-3>', '<Meta-Key-3>'],
|
||||
'<<uncomment-region>>': ['<Alt-Key-4>', '<Meta-Key-4>'],
|
||||
'<<tabify-region>>': ['<Alt-Key-5>', '<Meta-Key-5>'],
|
||||
'<<untabify-region>>': ['<Alt-Key-6>', '<Meta-Key-6>'],
|
||||
'<<toggle-tabs>>': ['<Alt-Key-t>'],
|
||||
'<<change-indentwidth>>': ['<Alt-Key-u>'],
|
||||
}
|
||||
|
||||
# usetabs true -> literal tab characters are used by indent and
|
||||
# dedent cmds, possibly mixed with spaces if
|
||||
# indentwidth is not a multiple of tabwidth
|
||||
# false -> tab characters are converted to spaces by indent
|
||||
# and dedent cmds, and ditto TAB keystrokes
|
||||
# indentwidth is the number of characters per logical indent level.
|
||||
# tabwidth is the display width of a literal tab character.
|
||||
# CAUTION: telling Tk to use anything other than its default
|
||||
# tab setting causes it to use an entirely different tabbing algorithm,
|
||||
# treating tab stops as fixed distances from the left margin.
|
||||
# Nobody expects this, so for now tabwidth should never be changed.
|
||||
usetabs = 1
|
||||
indentwidth = 4
|
||||
tabwidth = 8 # for IDLE use, must remain 8 until Tk is fixed
|
||||
|
||||
# If context_use_ps1 is true, parsing searches back for a ps1 line;
|
||||
# else searches for a popular (if, def, ...) Python stmt.
|
||||
context_use_ps1 = 0
|
||||
|
||||
# When searching backwards for a reliable place to begin parsing,
|
||||
# first start num_context_lines[0] lines back, then
|
||||
# num_context_lines[1] lines back if that didn't work, and so on.
|
||||
# The last value should be huge (larger than the # of lines in a
|
||||
# conceivable file).
|
||||
# Making the initial values larger slows things down more often.
|
||||
num_context_lines = 50, 500, 5000000
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
self.text = editwin.text
|
||||
|
||||
def config(self, **options):
|
||||
for key, value in options.items():
|
||||
if key == 'usetabs':
|
||||
self.usetabs = value
|
||||
elif key == 'indentwidth':
|
||||
self.indentwidth = value
|
||||
elif key == 'tabwidth':
|
||||
self.tabwidth = value
|
||||
elif key == 'context_use_ps1':
|
||||
self.context_use_ps1 = value
|
||||
else:
|
||||
raise KeyError, "bad option name: %s" % `key`
|
||||
|
||||
# If ispythonsource and guess are true, guess a good value for
|
||||
# indentwidth based on file content (if possible), and if
|
||||
# indentwidth != tabwidth set usetabs false.
|
||||
# In any case, adjust the Text widget's view of what a tab
|
||||
# character means.
|
||||
|
||||
def set_indentation_params(self, ispythonsource, guess=1):
|
||||
if guess and ispythonsource:
|
||||
i = self.guess_indent()
|
||||
if 2 <= i <= 8:
|
||||
self.indentwidth = i
|
||||
if self.indentwidth != self.tabwidth:
|
||||
self.usetabs = 0
|
||||
|
||||
self.editwin.set_tabwidth(self.tabwidth)
|
||||
|
||||
def smart_backspace_event(self, event):
|
||||
text = self.text
|
||||
first, last = self.editwin.get_selection_indices()
|
||||
if first and last:
|
||||
text.delete(first, last)
|
||||
text.mark_set("insert", first)
|
||||
return "break"
|
||||
# Delete whitespace left, until hitting a real char or closest
|
||||
# preceding virtual tab stop.
|
||||
chars = text.get("insert linestart", "insert")
|
||||
if chars == '':
|
||||
if text.compare("insert", ">", "1.0"):
|
||||
# easy: delete preceding newline
|
||||
text.delete("insert-1c")
|
||||
else:
|
||||
text.bell() # at start of buffer
|
||||
return "break"
|
||||
if chars[-1] not in " \t":
|
||||
# easy: delete preceding real char
|
||||
text.delete("insert-1c")
|
||||
return "break"
|
||||
# Ick. It may require *inserting* spaces if we back up over a
|
||||
# tab character! This is written to be clear, not fast.
|
||||
tabwidth = self.tabwidth
|
||||
have = len(chars.expandtabs(tabwidth))
|
||||
assert have > 0
|
||||
want = ((have - 1) // self.indentwidth) * self.indentwidth
|
||||
ncharsdeleted = 0
|
||||
while 1:
|
||||
chars = chars[:-1]
|
||||
ncharsdeleted = ncharsdeleted + 1
|
||||
have = len(chars.expandtabs(tabwidth))
|
||||
if have <= want or chars[-1] not in " \t":
|
||||
break
|
||||
text.undo_block_start()
|
||||
text.delete("insert-%dc" % ncharsdeleted, "insert")
|
||||
if have < want:
|
||||
text.insert("insert", ' ' * (want - have))
|
||||
text.undo_block_stop()
|
||||
return "break"
|
||||
|
||||
def smart_indent_event(self, event):
|
||||
# if intraline selection:
|
||||
# delete it
|
||||
# elif multiline selection:
|
||||
# do indent-region & return
|
||||
# indent one level
|
||||
text = self.text
|
||||
first, last = self.editwin.get_selection_indices()
|
||||
text.undo_block_start()
|
||||
try:
|
||||
if first and last:
|
||||
if index2line(first) != index2line(last):
|
||||
return self.indent_region_event(event)
|
||||
text.delete(first, last)
|
||||
text.mark_set("insert", first)
|
||||
prefix = text.get("insert linestart", "insert")
|
||||
raw, effective = classifyws(prefix, self.tabwidth)
|
||||
if raw == len(prefix):
|
||||
# only whitespace to the left
|
||||
self.reindent_to(effective + self.indentwidth)
|
||||
else:
|
||||
if self.usetabs:
|
||||
pad = '\t'
|
||||
else:
|
||||
effective = len(prefix.expandtabs(self.tabwidth))
|
||||
n = self.indentwidth
|
||||
pad = ' ' * (n - effective % n)
|
||||
text.insert("insert", pad)
|
||||
text.see("insert")
|
||||
return "break"
|
||||
finally:
|
||||
text.undo_block_stop()
|
||||
|
||||
def newline_and_indent_event(self, event):
|
||||
text = self.text
|
||||
first, last = self.editwin.get_selection_indices()
|
||||
text.undo_block_start()
|
||||
try:
|
||||
if first and last:
|
||||
text.delete(first, last)
|
||||
text.mark_set("insert", first)
|
||||
line = text.get("insert linestart", "insert")
|
||||
i, n = 0, len(line)
|
||||
while i < n and line[i] in " \t":
|
||||
i = i+1
|
||||
if i == n:
|
||||
# the cursor is in or at leading indentation; just inject
|
||||
# an empty line at the start
|
||||
text.insert("insert linestart", '\n')
|
||||
return "break"
|
||||
indent = line[:i]
|
||||
# strip whitespace before insert point
|
||||
i = 0
|
||||
while line and line[-1] in " \t":
|
||||
line = line[:-1]
|
||||
i = i+1
|
||||
if i:
|
||||
text.delete("insert - %d chars" % i, "insert")
|
||||
# strip whitespace after insert point
|
||||
while text.get("insert") in " \t":
|
||||
text.delete("insert")
|
||||
# start new line
|
||||
text.insert("insert", '\n')
|
||||
|
||||
# adjust indentation for continuations and block
|
||||
# open/close first need to find the last stmt
|
||||
lno = index2line(text.index('insert'))
|
||||
y = PyParse.Parser(self.indentwidth, self.tabwidth)
|
||||
for context in self.num_context_lines:
|
||||
startat = max(lno - context, 1)
|
||||
startatindex = `startat` + ".0"
|
||||
rawtext = text.get(startatindex, "insert")
|
||||
y.set_str(rawtext)
|
||||
bod = y.find_good_parse_start(
|
||||
self.context_use_ps1,
|
||||
self._build_char_in_string_func(startatindex))
|
||||
if bod is not None or startat == 1:
|
||||
break
|
||||
y.set_lo(bod or 0)
|
||||
c = y.get_continuation_type()
|
||||
if c != PyParse.C_NONE:
|
||||
# The current stmt hasn't ended yet.
|
||||
if c == PyParse.C_STRING:
|
||||
# inside a string; just mimic the current indent
|
||||
text.insert("insert", indent)
|
||||
elif c == PyParse.C_BRACKET:
|
||||
# line up with the first (if any) element of the
|
||||
# last open bracket structure; else indent one
|
||||
# level beyond the indent of the line with the
|
||||
# last open bracket
|
||||
self.reindent_to(y.compute_bracket_indent())
|
||||
elif c == PyParse.C_BACKSLASH:
|
||||
# if more than one line in this stmt already, just
|
||||
# mimic the current indent; else if initial line
|
||||
# has a start on an assignment stmt, indent to
|
||||
# beyond leftmost =; else to beyond first chunk of
|
||||
# non-whitespace on initial line
|
||||
if y.get_num_lines_in_stmt() > 1:
|
||||
text.insert("insert", indent)
|
||||
else:
|
||||
self.reindent_to(y.compute_backslash_indent())
|
||||
else:
|
||||
assert 0, "bogus continuation type " + `c`
|
||||
return "break"
|
||||
|
||||
# This line starts a brand new stmt; indent relative to
|
||||
# indentation of initial line of closest preceding
|
||||
# interesting stmt.
|
||||
indent = y.get_base_indent_string()
|
||||
text.insert("insert", indent)
|
||||
if y.is_block_opener():
|
||||
self.smart_indent_event(event)
|
||||
elif indent and y.is_block_closer():
|
||||
self.smart_backspace_event(event)
|
||||
return "break"
|
||||
finally:
|
||||
text.see("insert")
|
||||
text.undo_block_stop()
|
||||
|
||||
auto_indent = newline_and_indent_event
|
||||
|
||||
# Our editwin provides a is_char_in_string function that works
|
||||
# with a Tk text index, but PyParse only knows about offsets into
|
||||
# a string. This builds a function for PyParse that accepts an
|
||||
# offset.
|
||||
|
||||
def _build_char_in_string_func(self, startindex):
|
||||
def inner(offset, _startindex=startindex,
|
||||
_icis=self.editwin.is_char_in_string):
|
||||
return _icis(_startindex + "+%dc" % offset)
|
||||
return inner
|
||||
|
||||
def indent_region_event(self, event):
|
||||
head, tail, chars, lines = self.get_region()
|
||||
for pos in range(len(lines)):
|
||||
line = lines[pos]
|
||||
if line:
|
||||
raw, effective = classifyws(line, self.tabwidth)
|
||||
effective = effective + self.indentwidth
|
||||
lines[pos] = self._make_blanks(effective) + line[raw:]
|
||||
self.set_region(head, tail, chars, lines)
|
||||
return "break"
|
||||
|
||||
def dedent_region_event(self, event):
|
||||
head, tail, chars, lines = self.get_region()
|
||||
for pos in range(len(lines)):
|
||||
line = lines[pos]
|
||||
if line:
|
||||
raw, effective = classifyws(line, self.tabwidth)
|
||||
effective = max(effective - self.indentwidth, 0)
|
||||
lines[pos] = self._make_blanks(effective) + line[raw:]
|
||||
self.set_region(head, tail, chars, lines)
|
||||
return "break"
|
||||
|
||||
def comment_region_event(self, event):
|
||||
head, tail, chars, lines = self.get_region()
|
||||
for pos in range(len(lines) - 1):
|
||||
line = lines[pos]
|
||||
lines[pos] = '##' + line
|
||||
self.set_region(head, tail, chars, lines)
|
||||
|
||||
def uncomment_region_event(self, event):
|
||||
head, tail, chars, lines = self.get_region()
|
||||
for pos in range(len(lines)):
|
||||
line = lines[pos]
|
||||
if not line:
|
||||
continue
|
||||
if line[:2] == '##':
|
||||
line = line[2:]
|
||||
elif line[:1] == '#':
|
||||
line = line[1:]
|
||||
lines[pos] = line
|
||||
self.set_region(head, tail, chars, lines)
|
||||
|
||||
def tabify_region_event(self, event):
|
||||
head, tail, chars, lines = self.get_region()
|
||||
tabwidth = self._asktabwidth()
|
||||
for pos in range(len(lines)):
|
||||
line = lines[pos]
|
||||
if line:
|
||||
raw, effective = classifyws(line, tabwidth)
|
||||
ntabs, nspaces = divmod(effective, tabwidth)
|
||||
lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:]
|
||||
self.set_region(head, tail, chars, lines)
|
||||
|
||||
def untabify_region_event(self, event):
|
||||
head, tail, chars, lines = self.get_region()
|
||||
tabwidth = self._asktabwidth()
|
||||
for pos in range(len(lines)):
|
||||
lines[pos] = lines[pos].expandtabs(tabwidth)
|
||||
self.set_region(head, tail, chars, lines)
|
||||
|
||||
def toggle_tabs_event(self, event):
|
||||
if self.editwin.askyesno(
|
||||
"Toggle tabs",
|
||||
"Turn tabs " + ("on", "off")[self.usetabs] + "?",
|
||||
parent=self.text):
|
||||
self.usetabs = not self.usetabs
|
||||
return "break"
|
||||
|
||||
# XXX this isn't bound to anything -- see class tabwidth comments
|
||||
def change_tabwidth_event(self, event):
|
||||
new = self._asktabwidth()
|
||||
if new != self.tabwidth:
|
||||
self.tabwidth = new
|
||||
self.set_indentation_params(0, guess=0)
|
||||
return "break"
|
||||
|
||||
def change_indentwidth_event(self, event):
|
||||
new = self.editwin.askinteger(
|
||||
"Indent width",
|
||||
"New indent width (1-16)",
|
||||
parent=self.text,
|
||||
initialvalue=self.indentwidth,
|
||||
minvalue=1,
|
||||
maxvalue=16)
|
||||
if new and new != self.indentwidth:
|
||||
self.indentwidth = new
|
||||
return "break"
|
||||
|
||||
def get_region(self):
|
||||
text = self.text
|
||||
first, last = self.editwin.get_selection_indices()
|
||||
if first and last:
|
||||
head = text.index(first + " linestart")
|
||||
tail = text.index(last + "-1c lineend +1c")
|
||||
else:
|
||||
head = text.index("insert linestart")
|
||||
tail = text.index("insert lineend +1c")
|
||||
chars = text.get(head, tail)
|
||||
lines = chars.split("\n")
|
||||
return head, tail, chars, lines
|
||||
|
||||
def set_region(self, head, tail, chars, lines):
|
||||
text = self.text
|
||||
newchars = "\n".join(lines)
|
||||
if newchars == chars:
|
||||
text.bell()
|
||||
return
|
||||
text.tag_remove("sel", "1.0", "end")
|
||||
text.mark_set("insert", head)
|
||||
text.undo_block_start()
|
||||
text.delete(head, tail)
|
||||
text.insert(head, newchars)
|
||||
text.undo_block_stop()
|
||||
text.tag_add("sel", head, "insert")
|
||||
|
||||
# Make string that displays as n leading blanks.
|
||||
|
||||
def _make_blanks(self, n):
|
||||
if self.usetabs:
|
||||
ntabs, nspaces = divmod(n, self.tabwidth)
|
||||
return '\t' * ntabs + ' ' * nspaces
|
||||
else:
|
||||
return ' ' * n
|
||||
|
||||
# Delete from beginning of line to insert point, then reinsert
|
||||
# column logical (meaning use tabs if appropriate) spaces.
|
||||
|
||||
def reindent_to(self, column):
|
||||
text = self.text
|
||||
text.undo_block_start()
|
||||
if text.compare("insert linestart", "!=", "insert"):
|
||||
text.delete("insert linestart", "insert")
|
||||
if column:
|
||||
text.insert("insert", self._make_blanks(column))
|
||||
text.undo_block_stop()
|
||||
|
||||
def _asktabwidth(self):
|
||||
return self.editwin.askinteger(
|
||||
"Tab width",
|
||||
"Spaces per tab?",
|
||||
parent=self.text,
|
||||
initialvalue=self.tabwidth,
|
||||
minvalue=1,
|
||||
maxvalue=16) or self.tabwidth
|
||||
|
||||
# Guess indentwidth from text content.
|
||||
# Return guessed indentwidth. This should not be believed unless
|
||||
# it's in a reasonable range (e.g., it will be 0 if no indented
|
||||
# blocks are found).
|
||||
|
||||
def guess_indent(self):
|
||||
opener, indented = IndentSearcher(self.text, self.tabwidth).run()
|
||||
if opener and indented:
|
||||
raw, indentsmall = classifyws(opener, self.tabwidth)
|
||||
raw, indentlarge = classifyws(indented, self.tabwidth)
|
||||
else:
|
||||
indentsmall = indentlarge = 0
|
||||
return indentlarge - indentsmall
|
||||
|
||||
# "line.col" -> line, as an int
|
||||
def index2line(index):
|
||||
return int(float(index))
|
||||
|
||||
# Look at the leading whitespace in s.
|
||||
# Return pair (# of leading ws characters,
|
||||
# effective # of leading blanks after expanding
|
||||
# tabs to width tabwidth)
|
||||
|
||||
def classifyws(s, tabwidth):
|
||||
raw = effective = 0
|
||||
for ch in s:
|
||||
if ch == ' ':
|
||||
raw = raw + 1
|
||||
effective = effective + 1
|
||||
elif ch == '\t':
|
||||
raw = raw + 1
|
||||
effective = (effective // tabwidth + 1) * tabwidth
|
||||
else:
|
||||
break
|
||||
return raw, effective
|
||||
|
||||
import tokenize
|
||||
_tokenize = tokenize
|
||||
del tokenize
|
||||
|
||||
class IndentSearcher:
|
||||
|
||||
# .run() chews over the Text widget, looking for a block opener
|
||||
# and the stmt following it. Returns a pair,
|
||||
# (line containing block opener, line containing stmt)
|
||||
# Either or both may be None.
|
||||
|
||||
def __init__(self, text, tabwidth):
|
||||
self.text = text
|
||||
self.tabwidth = tabwidth
|
||||
self.i = self.finished = 0
|
||||
self.blkopenline = self.indentedline = None
|
||||
|
||||
def readline(self):
|
||||
if self.finished:
|
||||
return ""
|
||||
i = self.i = self.i + 1
|
||||
mark = `i` + ".0"
|
||||
if self.text.compare(mark, ">=", "end"):
|
||||
return ""
|
||||
return self.text.get(mark, mark + " lineend+1c")
|
||||
|
||||
def tokeneater(self, type, token, start, end, line,
|
||||
INDENT=_tokenize.INDENT,
|
||||
NAME=_tokenize.NAME,
|
||||
OPENERS=('class', 'def', 'for', 'if', 'try', 'while')):
|
||||
if self.finished:
|
||||
pass
|
||||
elif type == NAME and token in OPENERS:
|
||||
self.blkopenline = line
|
||||
elif type == INDENT and self.blkopenline:
|
||||
self.indentedline = line
|
||||
self.finished = 1
|
||||
|
||||
def run(self):
|
||||
save_tabsize = _tokenize.tabsize
|
||||
_tokenize.tabsize = self.tabwidth
|
||||
try:
|
||||
try:
|
||||
_tokenize.tokenize(self.readline, self.tokeneater)
|
||||
except _tokenize.TokenError:
|
||||
# since we cut off the tokenizer early, we can trigger
|
||||
# spurious errors
|
||||
pass
|
||||
finally:
|
||||
_tokenize.tabsize = save_tabsize
|
||||
return self.blkopenline, self.indentedline
|
|
@ -1,58 +0,0 @@
|
|||
# This file defines the menu contents and key bindings. Note that
|
||||
# there is additional configuration information in the EditorWindow
|
||||
# class (and subclasses): the menus are created there based on the
|
||||
# menu_specs (class) variable, and menus not created are silently
|
||||
# skipped by the code here. This makes it possible to define the
|
||||
# Debug menu here, which is only present in the PythonShell window.
|
||||
|
||||
import sys
|
||||
from keydefs import *
|
||||
|
||||
menudefs = [
|
||||
# underscore prefixes character to underscore
|
||||
('file', [
|
||||
('_New window', '<<open-new-window>>'),
|
||||
('_Open...', '<<open-window-from-file>>'),
|
||||
('Open _module...', '<<open-module>>'),
|
||||
('Class _browser', '<<open-class-browser>>'),
|
||||
('_Path browser', '<<open-path-browser>>'),
|
||||
('Python shell', '<<open-python-shell>>'),
|
||||
None,
|
||||
('_Save', '<<save-window>>'),
|
||||
('Save _As...', '<<save-window-as-file>>'),
|
||||
('Save Co_py As...', '<<save-copy-of-window-as-file>>'),
|
||||
None,
|
||||
('_Print window', '<<print-window>>'),
|
||||
None,
|
||||
('_Close', '<<close-window>>'),
|
||||
('E_xit', '<<close-all-windows>>'),
|
||||
]),
|
||||
('edit', [
|
||||
('_Undo', '<<undo>>'),
|
||||
('_Redo', '<<redo>>'),
|
||||
None,
|
||||
('Cu_t', '<<Cut>>'),
|
||||
('_Copy', '<<Copy>>'),
|
||||
('_Paste', '<<Paste>>'),
|
||||
('Select _All', '<<select-all>>'),
|
||||
]),
|
||||
('debug', [
|
||||
('_Go to file/line', '<<goto-file-line>>'),
|
||||
('_Stack viewer', '<<open-stack-viewer>>'),
|
||||
('!_Debugger', '<<toggle-debugger>>'),
|
||||
('!_Auto-open stack viewer', '<<toggle-jit-stack-viewer>>' ),
|
||||
]),
|
||||
('help', [
|
||||
('_Help...', '<<help>>'),
|
||||
('Python _Documentation...', '<<python-docs>>'),
|
||||
None,
|
||||
('_About IDLE...', '<<about-idle>>'),
|
||||
]),
|
||||
]
|
||||
|
||||
if sys.platform == 'win32':
|
||||
default_keydefs = windows_keydefs
|
||||
else:
|
||||
default_keydefs = unix_keydefs
|
||||
|
||||
del sys
|
|
@ -1,76 +0,0 @@
|
|||
# A CallTip window class for Tkinter/IDLE.
|
||||
# After ToolTip.py, which uses ideas gleaned from PySol
|
||||
|
||||
# Used by the CallTips IDLE extension.
|
||||
from Tkinter import *
|
||||
|
||||
class CallTip:
|
||||
|
||||
def __init__(self, widget):
|
||||
self.widget = widget
|
||||
self.tipwindow = None
|
||||
self.id = None
|
||||
self.x = self.y = 0
|
||||
|
||||
def showtip(self, text):
|
||||
# SF bug 546078: IDLE calltips cause application error.
|
||||
# There were crashes on various Windows flavors, and even a
|
||||
# crashing X server on Linux, with very long calltips.
|
||||
if len(text) >= 79:
|
||||
text = text[:75] + ' ...'
|
||||
self.text = text
|
||||
|
||||
if self.tipwindow or not self.text:
|
||||
return
|
||||
self.widget.see("insert")
|
||||
x, y, cx, cy = self.widget.bbox("insert")
|
||||
x = x + self.widget.winfo_rootx() + 2
|
||||
y = y + cy + self.widget.winfo_rooty()
|
||||
self.tipwindow = tw = Toplevel(self.widget)
|
||||
tw.wm_overrideredirect(1)
|
||||
tw.wm_geometry("+%d+%d" % (x, y))
|
||||
label = Label(tw, text=self.text, justify=LEFT,
|
||||
background="#ffffe0", relief=SOLID, borderwidth=1,
|
||||
font = self.widget['font'])
|
||||
label.pack()
|
||||
|
||||
def hidetip(self):
|
||||
tw = self.tipwindow
|
||||
self.tipwindow = None
|
||||
if tw:
|
||||
tw.destroy()
|
||||
|
||||
|
||||
###############################
|
||||
#
|
||||
# Test Code
|
||||
#
|
||||
class container: # Conceptually an editor_window
|
||||
def __init__(self):
|
||||
root = Tk()
|
||||
text = self.text = Text(root)
|
||||
text.pack(side=LEFT, fill=BOTH, expand=1)
|
||||
text.insert("insert", "string.split")
|
||||
root.update()
|
||||
self.calltip = CallTip(text)
|
||||
|
||||
text.event_add("<<calltip-show>>", "(")
|
||||
text.event_add("<<calltip-hide>>", ")")
|
||||
text.bind("<<calltip-show>>", self.calltip_show)
|
||||
text.bind("<<calltip-hide>>", self.calltip_hide)
|
||||
|
||||
text.focus_set()
|
||||
# root.mainloop() # not in idle
|
||||
|
||||
def calltip_show(self, event):
|
||||
self.calltip.showtip("Hello world")
|
||||
|
||||
def calltip_hide(self, event):
|
||||
self.calltip.hidetip()
|
||||
|
||||
def main():
|
||||
# Test code
|
||||
c=container()
|
||||
|
||||
if __name__=='__main__':
|
||||
main()
|
|
@ -1,197 +0,0 @@
|
|||
# CallTips.py - An IDLE extension that provides "Call Tips" - ie, a floating window that
|
||||
# displays parameter information as you open parens.
|
||||
|
||||
import string
|
||||
import types
|
||||
|
||||
class CallTips:
|
||||
|
||||
menudefs = [
|
||||
]
|
||||
|
||||
keydefs = {
|
||||
'<<paren-open>>': ['<Key-parenleft>'],
|
||||
'<<paren-close>>': ['<Key-parenright>'],
|
||||
'<<check-calltip-cancel>>': ['<KeyRelease>'],
|
||||
'<<calltip-cancel>>': ['<ButtonPress>', '<Key-Escape>'],
|
||||
}
|
||||
|
||||
windows_keydefs = {
|
||||
}
|
||||
|
||||
unix_keydefs = {
|
||||
}
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
self.text = editwin.text
|
||||
self.calltip = None
|
||||
if hasattr(self.text, "make_calltip_window"):
|
||||
self._make_calltip_window = self.text.make_calltip_window
|
||||
else:
|
||||
self._make_calltip_window = self._make_tk_calltip_window
|
||||
|
||||
def close(self):
|
||||
self._make_calltip_window = None
|
||||
|
||||
# Makes a Tk based calltip window. Used by IDLE, but not Pythonwin.
|
||||
# See __init__ above for how this is used.
|
||||
def _make_tk_calltip_window(self):
|
||||
import CallTipWindow
|
||||
return CallTipWindow.CallTip(self.text)
|
||||
|
||||
def _remove_calltip_window(self):
|
||||
if self.calltip:
|
||||
self.calltip.hidetip()
|
||||
self.calltip = None
|
||||
|
||||
def paren_open_event(self, event):
|
||||
self._remove_calltip_window()
|
||||
arg_text = get_arg_text(self.get_object_at_cursor())
|
||||
if arg_text:
|
||||
self.calltip_start = self.text.index("insert")
|
||||
self.calltip = self._make_calltip_window()
|
||||
self.calltip.showtip(arg_text)
|
||||
return "" #so the event is handled normally.
|
||||
|
||||
def paren_close_event(self, event):
|
||||
# Now just hides, but later we should check if other
|
||||
# paren'd expressions remain open.
|
||||
self._remove_calltip_window()
|
||||
return "" #so the event is handled normally.
|
||||
|
||||
def check_calltip_cancel_event(self, event):
|
||||
if self.calltip:
|
||||
# If we have moved before the start of the calltip,
|
||||
# or off the calltip line, then cancel the tip.
|
||||
# (Later need to be smarter about multi-line, etc)
|
||||
if self.text.compare("insert", "<=", self.calltip_start) or \
|
||||
self.text.compare("insert", ">", self.calltip_start + " lineend"):
|
||||
self._remove_calltip_window()
|
||||
return "" #so the event is handled normally.
|
||||
|
||||
def calltip_cancel_event(self, event):
|
||||
self._remove_calltip_window()
|
||||
return "" #so the event is handled normally.
|
||||
|
||||
def get_object_at_cursor(self,
|
||||
wordchars="._" + string.ascii_letters + string.digits):
|
||||
# Usage of ascii_letters is necessary to avoid UnicodeErrors
|
||||
# if chars contains non-ASCII.
|
||||
|
||||
# XXX - This needs to be moved to a better place
|
||||
# so the "." attribute lookup code can also use it.
|
||||
text = self.text
|
||||
chars = text.get("insert linestart", "insert")
|
||||
i = len(chars)
|
||||
while i and chars[i-1] in wordchars:
|
||||
i = i-1
|
||||
word = chars[i:]
|
||||
if word:
|
||||
# How is this for a hack!
|
||||
import sys, __main__
|
||||
namespace = sys.modules.copy()
|
||||
namespace.update(__main__.__dict__)
|
||||
try:
|
||||
return eval(word, namespace)
|
||||
except:
|
||||
pass
|
||||
return None # Can't find an object.
|
||||
|
||||
def _find_constructor(class_ob):
|
||||
# Given a class object, return a function object used for the
|
||||
# constructor (ie, __init__() ) or None if we can't find one.
|
||||
try:
|
||||
return class_ob.__init__.im_func
|
||||
except AttributeError:
|
||||
for base in class_ob.__bases__:
|
||||
rc = _find_constructor(base)
|
||||
if rc is not None: return rc
|
||||
return None
|
||||
|
||||
def get_arg_text(ob):
|
||||
# Get a string describing the arguments for the given object.
|
||||
argText = ""
|
||||
if ob is not None:
|
||||
argOffset = 0
|
||||
if type(ob)==types.ClassType:
|
||||
# Look for the highest __init__ in the class chain.
|
||||
fob = _find_constructor(ob)
|
||||
if fob is None:
|
||||
fob = lambda: None
|
||||
else:
|
||||
argOffset = 1
|
||||
elif type(ob)==types.MethodType:
|
||||
# bit of a hack for methods - turn it into a function
|
||||
# but we drop the "self" param.
|
||||
fob = ob.im_func
|
||||
argOffset = 1
|
||||
else:
|
||||
fob = ob
|
||||
# Try and build one for Python defined functions
|
||||
if type(fob) in [types.FunctionType, types.LambdaType]:
|
||||
try:
|
||||
realArgs = fob.func_code.co_varnames[argOffset:fob.func_code.co_argcount]
|
||||
defaults = fob.func_defaults or []
|
||||
defaults = list(map(lambda name: "=%s" % name, defaults))
|
||||
defaults = [""] * (len(realArgs)-len(defaults)) + defaults
|
||||
items = map(lambda arg, dflt: arg+dflt, realArgs, defaults)
|
||||
if fob.func_code.co_flags & 0x4:
|
||||
items.append("...")
|
||||
if fob.func_code.co_flags & 0x8:
|
||||
items.append("***")
|
||||
argText = ", ".join(items)
|
||||
argText = "(%s)" % argText
|
||||
except:
|
||||
pass
|
||||
# See if we can use the docstring
|
||||
doc = getattr(ob, "__doc__", "")
|
||||
if doc:
|
||||
while doc[:1] in " \t\n":
|
||||
doc = doc[1:]
|
||||
pos = doc.find("\n")
|
||||
if pos < 0 or pos > 70:
|
||||
pos = 70
|
||||
if argText:
|
||||
argText += "\n"
|
||||
argText += doc[:pos]
|
||||
|
||||
return argText
|
||||
|
||||
#################################################
|
||||
#
|
||||
# Test code
|
||||
#
|
||||
if __name__=='__main__':
|
||||
|
||||
def t1(): "()"
|
||||
def t2(a, b=None): "(a, b=None)"
|
||||
def t3(a, *args): "(a, ...)"
|
||||
def t4(*args): "(...)"
|
||||
def t5(a, *args): "(a, ...)"
|
||||
def t6(a, b=None, *args, **kw): "(a, b=None, ..., ***)"
|
||||
|
||||
class TC:
|
||||
"(a=None, ...)"
|
||||
def __init__(self, a=None, *b): "(a=None, ...)"
|
||||
def t1(self): "()"
|
||||
def t2(self, a, b=None): "(a, b=None)"
|
||||
def t3(self, a, *args): "(a, ...)"
|
||||
def t4(self, *args): "(...)"
|
||||
def t5(self, a, *args): "(a, ...)"
|
||||
def t6(self, a, b=None, *args, **kw): "(a, b=None, ..., ***)"
|
||||
|
||||
def test( tests ):
|
||||
failed=[]
|
||||
for t in tests:
|
||||
expected = t.__doc__ + "\n" + t.__doc__
|
||||
if get_arg_text(t) != expected:
|
||||
failed.append(t)
|
||||
print "%s - expected %s, but got %s" % (t, `expected`, `get_arg_text(t)`)
|
||||
print "%d of %d tests failed" % (len(failed), len(tests))
|
||||
|
||||
tc = TC()
|
||||
tests = t1, t2, t3, t4, t5, t6, \
|
||||
TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6
|
||||
|
||||
test(tests)
|
1017
Tools/idle/ChangeLog
1017
Tools/idle/ChangeLog
File diff suppressed because it is too large
Load Diff
|
@ -1,217 +0,0 @@
|
|||
"""Class browser.
|
||||
|
||||
XXX TO DO:
|
||||
|
||||
- reparse when source changed (maybe just a button would be OK?)
|
||||
(or recheck on window popup)
|
||||
- add popup menu with more options (e.g. doc strings, base classes, imports)
|
||||
- show function argument list? (have to do pattern matching on source)
|
||||
- should the classes and methods lists also be in the module's menu bar?
|
||||
- add base classes to class browser tree
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import pyclbr
|
||||
|
||||
import PyShell
|
||||
from WindowList import ListedToplevel
|
||||
from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
|
||||
|
||||
class ClassBrowser:
|
||||
|
||||
def __init__(self, flist, name, path):
|
||||
# XXX This API should change, if the file doesn't end in ".py"
|
||||
# XXX the code here is bogus!
|
||||
self.name = name
|
||||
self.file = os.path.join(path[0], self.name + ".py")
|
||||
self.init(flist)
|
||||
|
||||
def close(self, event=None):
|
||||
self.top.destroy()
|
||||
self.node.destroy()
|
||||
|
||||
def init(self, flist):
|
||||
self.flist = flist
|
||||
# reset pyclbr
|
||||
pyclbr._modules.clear()
|
||||
# create top
|
||||
self.top = top = ListedToplevel(flist.root)
|
||||
top.protocol("WM_DELETE_WINDOW", self.close)
|
||||
top.bind("<Escape>", self.close)
|
||||
self.settitle()
|
||||
top.focus_set()
|
||||
# create scrolled canvas
|
||||
sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1)
|
||||
sc.frame.pack(expand=1, fill="both")
|
||||
item = self.rootnode()
|
||||
self.node = node = TreeNode(sc.canvas, None, item)
|
||||
node.update()
|
||||
node.expand()
|
||||
|
||||
def settitle(self):
|
||||
self.top.wm_title("Class Browser - " + self.name)
|
||||
self.top.wm_iconname("Class Browser")
|
||||
|
||||
def rootnode(self):
|
||||
return ModuleBrowserTreeItem(self.file)
|
||||
|
||||
class ModuleBrowserTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, file):
|
||||
self.file = file
|
||||
|
||||
def GetText(self):
|
||||
return os.path.basename(self.file)
|
||||
|
||||
def GetIconName(self):
|
||||
return "python"
|
||||
|
||||
def GetSubList(self):
|
||||
sublist = []
|
||||
for name in self.listclasses():
|
||||
item = ClassBrowserTreeItem(name, self.classes, self.file)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
def OnDoubleClick(self):
|
||||
if os.path.normcase(self.file[-3:]) != ".py":
|
||||
return
|
||||
if not os.path.exists(self.file):
|
||||
return
|
||||
PyShell.flist.open(self.file)
|
||||
|
||||
def IsExpandable(self):
|
||||
return os.path.normcase(self.file[-3:]) == ".py"
|
||||
|
||||
def listclasses(self):
|
||||
dir, file = os.path.split(self.file)
|
||||
name, ext = os.path.splitext(file)
|
||||
if os.path.normcase(ext) != ".py":
|
||||
return []
|
||||
try:
|
||||
dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
|
||||
except ImportError, msg:
|
||||
return []
|
||||
items = []
|
||||
self.classes = {}
|
||||
for key, cl in dict.items():
|
||||
if cl.module == name:
|
||||
s = key
|
||||
if hasattr(cl, "super") and cl.super:
|
||||
supers = []
|
||||
for sup in cl.super:
|
||||
if type(sup) is type(''):
|
||||
sname = sup
|
||||
else:
|
||||
sname = sup.name
|
||||
if sup.module != cl.module:
|
||||
sname = "%s.%s" % (sup.module, sname)
|
||||
supers.append(sname)
|
||||
s = s + "(%s)" % ", ".join(supers)
|
||||
items.append((cl.lineno, s))
|
||||
self.classes[s] = cl
|
||||
items.sort()
|
||||
list = []
|
||||
for item, s in items:
|
||||
list.append(s)
|
||||
return list
|
||||
|
||||
class ClassBrowserTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, name, classes, file):
|
||||
self.name = name
|
||||
self.classes = classes
|
||||
self.file = file
|
||||
try:
|
||||
self.cl = self.classes[self.name]
|
||||
except (IndexError, KeyError):
|
||||
self.cl = None
|
||||
self.isfunction = isinstance(self.cl, pyclbr.Function)
|
||||
|
||||
def GetText(self):
|
||||
if self.isfunction:
|
||||
return "def " + self.name + "(...)"
|
||||
else:
|
||||
return "class " + self.name
|
||||
|
||||
def GetIconName(self):
|
||||
if self.isfunction:
|
||||
return "python"
|
||||
else:
|
||||
return "folder"
|
||||
|
||||
def IsExpandable(self):
|
||||
try:
|
||||
return bool(self.cl.methods)
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
def GetSubList(self):
|
||||
if not self.cl:
|
||||
return []
|
||||
sublist = []
|
||||
for name in self.listmethods():
|
||||
item = MethodBrowserTreeItem(name, self.cl, self.file)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
def OnDoubleClick(self):
|
||||
if not os.path.exists(self.file):
|
||||
return
|
||||
edit = PyShell.flist.open(self.file)
|
||||
if hasattr(self.cl, 'lineno'):
|
||||
lineno = self.cl.lineno
|
||||
edit.gotoline(lineno)
|
||||
|
||||
def listmethods(self):
|
||||
if not self.cl:
|
||||
return []
|
||||
items = []
|
||||
for name, lineno in self.cl.methods.items():
|
||||
items.append((lineno, name))
|
||||
items.sort()
|
||||
list = []
|
||||
for item, name in items:
|
||||
list.append(name)
|
||||
return list
|
||||
|
||||
class MethodBrowserTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, name, cl, file):
|
||||
self.name = name
|
||||
self.cl = cl
|
||||
self.file = file
|
||||
|
||||
def GetText(self):
|
||||
return "def " + self.name + "(...)"
|
||||
|
||||
def GetIconName(self):
|
||||
return "python" # XXX
|
||||
|
||||
def IsExpandable(self):
|
||||
return 0
|
||||
|
||||
def OnDoubleClick(self):
|
||||
if not os.path.exists(self.file):
|
||||
return
|
||||
edit = PyShell.flist.open(self.file)
|
||||
edit.gotoline(self.cl.methods[self.name])
|
||||
|
||||
def main():
|
||||
try:
|
||||
file = __file__
|
||||
except NameError:
|
||||
file = sys.argv[0]
|
||||
if sys.argv[1:]:
|
||||
file = sys.argv[1]
|
||||
else:
|
||||
file = sys.argv[0]
|
||||
dir, file = os.path.split(file)
|
||||
name = os.path.splitext(file)[0]
|
||||
ClassBrowser(PyShell.flist, name, [dir])
|
||||
if sys.stdin is sys.__stdin__:
|
||||
mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,246 +0,0 @@
|
|||
import time
|
||||
import re
|
||||
import keyword
|
||||
from Tkinter import *
|
||||
from Delegator import Delegator
|
||||
from IdleConf import idleconf
|
||||
|
||||
#$ event <<toggle-auto-coloring>>
|
||||
#$ win <Control-slash>
|
||||
#$ unix <Control-slash>
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
|
||||
def any(name, list):
|
||||
return "(?P<%s>" % name + "|".join(list) + ")"
|
||||
|
||||
def make_pat():
|
||||
kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b"
|
||||
comment = any("COMMENT", [r"#[^\n]*"])
|
||||
sqstring = r"(\b[rR])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
|
||||
dqstring = r'(\b[rR])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
|
||||
sq3string = r"(\b[rR])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
|
||||
dq3string = r'(\b[rR])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
|
||||
string = any("STRING", [sq3string, dq3string, sqstring, dqstring])
|
||||
return kw + "|" + comment + "|" + string + "|" + any("SYNC", [r"\n"])
|
||||
|
||||
prog = re.compile(make_pat(), re.S)
|
||||
idprog = re.compile(r"\s+(\w+)", re.S)
|
||||
asprog = re.compile(r".*?\b(as)\b", re.S)
|
||||
|
||||
class ColorDelegator(Delegator):
|
||||
|
||||
def __init__(self):
|
||||
Delegator.__init__(self)
|
||||
self.prog = prog
|
||||
self.idprog = idprog
|
||||
self.asprog = asprog
|
||||
|
||||
def setdelegate(self, delegate):
|
||||
if self.delegate is not None:
|
||||
self.unbind("<<toggle-auto-coloring>>")
|
||||
Delegator.setdelegate(self, delegate)
|
||||
if delegate is not None:
|
||||
self.config_colors()
|
||||
self.bind("<<toggle-auto-coloring>>", self.toggle_colorize_event)
|
||||
self.notify_range("1.0", "end")
|
||||
|
||||
def config_colors(self):
|
||||
for tag, cnf in self.tagdefs.items():
|
||||
if cnf:
|
||||
apply(self.tag_configure, (tag,), cnf)
|
||||
self.tag_raise('sel')
|
||||
|
||||
cconf = idleconf.getsection('Colors')
|
||||
|
||||
tagdefs = {
|
||||
"COMMENT": cconf.getcolor("comment"),
|
||||
"KEYWORD": cconf.getcolor("keyword"),
|
||||
"STRING": cconf.getcolor("string"),
|
||||
"DEFINITION": cconf.getcolor("definition"),
|
||||
"SYNC": cconf.getcolor("sync"),
|
||||
"TODO": cconf.getcolor("todo"),
|
||||
"BREAK": cconf.getcolor("break"),
|
||||
# The following is used by ReplaceDialog:
|
||||
"hit": cconf.getcolor("hit"),
|
||||
}
|
||||
|
||||
def insert(self, index, chars, tags=None):
|
||||
index = self.index(index)
|
||||
self.delegate.insert(index, chars, tags)
|
||||
self.notify_range(index, index + "+%dc" % len(chars))
|
||||
|
||||
def delete(self, index1, index2=None):
|
||||
index1 = self.index(index1)
|
||||
self.delegate.delete(index1, index2)
|
||||
self.notify_range(index1)
|
||||
|
||||
after_id = None
|
||||
allow_colorizing = 1
|
||||
colorizing = 0
|
||||
|
||||
def notify_range(self, index1, index2=None):
|
||||
self.tag_add("TODO", index1, index2)
|
||||
if self.after_id:
|
||||
if DEBUG: print "colorizing already scheduled"
|
||||
return
|
||||
if self.colorizing:
|
||||
self.stop_colorizing = 1
|
||||
if DEBUG: print "stop colorizing"
|
||||
if self.allow_colorizing:
|
||||
if DEBUG: print "schedule colorizing"
|
||||
self.after_id = self.after(1, self.recolorize)
|
||||
|
||||
close_when_done = None # Window to be closed when done colorizing
|
||||
|
||||
def close(self, close_when_done=None):
|
||||
if self.after_id:
|
||||
after_id = self.after_id
|
||||
self.after_id = None
|
||||
if DEBUG: print "cancel scheduled recolorizer"
|
||||
self.after_cancel(after_id)
|
||||
self.allow_colorizing = 0
|
||||
self.stop_colorizing = 1
|
||||
if close_when_done:
|
||||
if not self.colorizing:
|
||||
close_when_done.destroy()
|
||||
else:
|
||||
self.close_when_done = close_when_done
|
||||
|
||||
def toggle_colorize_event(self, event):
|
||||
if self.after_id:
|
||||
after_id = self.after_id
|
||||
self.after_id = None
|
||||
if DEBUG: print "cancel scheduled recolorizer"
|
||||
self.after_cancel(after_id)
|
||||
if self.allow_colorizing and self.colorizing:
|
||||
if DEBUG: print "stop colorizing"
|
||||
self.stop_colorizing = 1
|
||||
self.allow_colorizing = not self.allow_colorizing
|
||||
if self.allow_colorizing and not self.colorizing:
|
||||
self.after_id = self.after(1, self.recolorize)
|
||||
if DEBUG:
|
||||
print "auto colorizing turned", self.allow_colorizing and "on" or "off"
|
||||
return "break"
|
||||
|
||||
def recolorize(self):
|
||||
self.after_id = None
|
||||
if not self.delegate:
|
||||
if DEBUG: print "no delegate"
|
||||
return
|
||||
if not self.allow_colorizing:
|
||||
if DEBUG: print "auto colorizing is off"
|
||||
return
|
||||
if self.colorizing:
|
||||
if DEBUG: print "already colorizing"
|
||||
return
|
||||
try:
|
||||
self.stop_colorizing = 0
|
||||
self.colorizing = 1
|
||||
if DEBUG: print "colorizing..."
|
||||
t0 = time.clock()
|
||||
self.recolorize_main()
|
||||
t1 = time.clock()
|
||||
if DEBUG: print "%.3f seconds" % (t1-t0)
|
||||
finally:
|
||||
self.colorizing = 0
|
||||
if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"):
|
||||
if DEBUG: print "reschedule colorizing"
|
||||
self.after_id = self.after(1, self.recolorize)
|
||||
if self.close_when_done:
|
||||
top = self.close_when_done
|
||||
self.close_when_done = None
|
||||
top.destroy()
|
||||
|
||||
def recolorize_main(self):
|
||||
next = "1.0"
|
||||
while 1:
|
||||
item = self.tag_nextrange("TODO", next)
|
||||
if not item:
|
||||
break
|
||||
head, tail = item
|
||||
self.tag_remove("SYNC", head, tail)
|
||||
item = self.tag_prevrange("SYNC", head)
|
||||
if item:
|
||||
head = item[1]
|
||||
else:
|
||||
head = "1.0"
|
||||
|
||||
chars = ""
|
||||
next = head
|
||||
lines_to_get = 1
|
||||
ok = 0
|
||||
while not ok:
|
||||
mark = next
|
||||
next = self.index(mark + "+%d lines linestart" %
|
||||
lines_to_get)
|
||||
lines_to_get = min(lines_to_get * 2, 100)
|
||||
ok = "SYNC" in self.tag_names(next + "-1c")
|
||||
line = self.get(mark, next)
|
||||
##print head, "get", mark, next, "->", `line`
|
||||
if not line:
|
||||
return
|
||||
for tag in self.tagdefs.keys():
|
||||
self.tag_remove(tag, mark, next)
|
||||
chars = chars + line
|
||||
m = self.prog.search(chars)
|
||||
while m:
|
||||
for key, value in m.groupdict().items():
|
||||
if value:
|
||||
a, b = m.span(key)
|
||||
self.tag_add(key,
|
||||
head + "+%dc" % a,
|
||||
head + "+%dc" % b)
|
||||
if value in ("def", "class"):
|
||||
m1 = self.idprog.match(chars, b)
|
||||
if m1:
|
||||
a, b = m1.span(1)
|
||||
self.tag_add("DEFINITION",
|
||||
head + "+%dc" % a,
|
||||
head + "+%dc" % b)
|
||||
elif value == "import":
|
||||
# color all the "as" words on same line;
|
||||
# cheap approximation to the truth
|
||||
while 1:
|
||||
m1 = self.asprog.match(chars, b)
|
||||
if not m1:
|
||||
break
|
||||
a, b = m1.span(1)
|
||||
self.tag_add("KEYWORD",
|
||||
head + "+%dc" % a,
|
||||
head + "+%dc" % b)
|
||||
m = self.prog.search(chars, m.end())
|
||||
if "SYNC" in self.tag_names(next + "-1c"):
|
||||
head = next
|
||||
chars = ""
|
||||
else:
|
||||
ok = 0
|
||||
if not ok:
|
||||
# We're in an inconsistent state, and the call to
|
||||
# update may tell us to stop. It may also change
|
||||
# the correct value for "next" (since this is a
|
||||
# line.col string, not a true mark). So leave a
|
||||
# crumb telling the next invocation to resume here
|
||||
# in case update tells us to leave.
|
||||
self.tag_add("TODO", next)
|
||||
self.update()
|
||||
if self.stop_colorizing:
|
||||
if DEBUG: print "colorizing stopped"
|
||||
return
|
||||
|
||||
|
||||
def main():
|
||||
from Percolator import Percolator
|
||||
root = Tk()
|
||||
root.wm_protocol("WM_DELETE_WINDOW", root.quit)
|
||||
text = Text(background="white")
|
||||
text.pack(expand=1, fill="both")
|
||||
text.focus_set()
|
||||
p = Percolator(text)
|
||||
d = ColorDelegator()
|
||||
p.insertfilter(d)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,308 +0,0 @@
|
|||
import os
|
||||
import bdb
|
||||
from Tkinter import *
|
||||
from WindowList import ListedToplevel
|
||||
|
||||
import StackViewer
|
||||
|
||||
|
||||
class Debugger(bdb.Bdb):
|
||||
|
||||
interacting = 0
|
||||
|
||||
vstack = vsource = vlocals = vglobals = None
|
||||
|
||||
def __init__(self, pyshell):
|
||||
bdb.Bdb.__init__(self)
|
||||
self.pyshell = pyshell
|
||||
self.make_gui()
|
||||
|
||||
def canonic(self, filename):
|
||||
# Canonicalize filename -- called by Bdb
|
||||
return os.path.normcase(os.path.abspath(filename))
|
||||
|
||||
def close(self, event=None):
|
||||
if self.interacting:
|
||||
self.top.bell()
|
||||
return
|
||||
if self.stackviewer:
|
||||
self.stackviewer.close(); self.stackviewer = None
|
||||
self.pyshell.close_debugger()
|
||||
self.top.destroy()
|
||||
|
||||
def run(self, *args):
|
||||
try:
|
||||
self.interacting = 1
|
||||
return apply(bdb.Bdb.run, (self,) + args)
|
||||
finally:
|
||||
self.interacting = 0
|
||||
|
||||
def user_line(self, frame):
|
||||
self.interaction(frame)
|
||||
|
||||
def user_return(self, frame, rv):
|
||||
# XXX show rv?
|
||||
##self.interaction(frame)
|
||||
pass
|
||||
|
||||
def user_exception(self, frame, info):
|
||||
self.interaction(frame, info)
|
||||
|
||||
def make_gui(self):
|
||||
pyshell = self.pyshell
|
||||
self.flist = pyshell.flist
|
||||
self.root = root = pyshell.root
|
||||
self.top = top =ListedToplevel(root)
|
||||
self.top.wm_title("Debug Control")
|
||||
self.top.wm_iconname("Debug")
|
||||
top.wm_protocol("WM_DELETE_WINDOW", self.close)
|
||||
self.top.bind("<Escape>", self.close)
|
||||
#
|
||||
self.bframe = bframe = Frame(top)
|
||||
self.bframe.pack(anchor="w")
|
||||
self.buttons = bl = []
|
||||
#
|
||||
self.bcont = b = Button(bframe, text="Go", command=self.cont)
|
||||
bl.append(b)
|
||||
self.bstep = b = Button(bframe, text="Step", command=self.step)
|
||||
bl.append(b)
|
||||
self.bnext = b = Button(bframe, text="Over", command=self.next)
|
||||
bl.append(b)
|
||||
self.bret = b = Button(bframe, text="Out", command=self.ret)
|
||||
bl.append(b)
|
||||
self.bret = b = Button(bframe, text="Quit", command=self.quit)
|
||||
bl.append(b)
|
||||
#
|
||||
for b in bl:
|
||||
b.configure(state="disabled")
|
||||
b.pack(side="left")
|
||||
#
|
||||
self.cframe = cframe = Frame(bframe)
|
||||
self.cframe.pack(side="left")
|
||||
#
|
||||
if not self.vstack:
|
||||
self.__class__.vstack = BooleanVar(top)
|
||||
self.vstack.set(1)
|
||||
self.bstack = Checkbutton(cframe,
|
||||
text="Stack", command=self.show_stack, variable=self.vstack)
|
||||
self.bstack.grid(row=0, column=0)
|
||||
if not self.vsource:
|
||||
self.__class__.vsource = BooleanVar(top)
|
||||
##self.vsource.set(1)
|
||||
self.bsource = Checkbutton(cframe,
|
||||
text="Source", command=self.show_source, variable=self.vsource)
|
||||
self.bsource.grid(row=0, column=1)
|
||||
if not self.vlocals:
|
||||
self.__class__.vlocals = BooleanVar(top)
|
||||
self.vlocals.set(1)
|
||||
self.blocals = Checkbutton(cframe,
|
||||
text="Locals", command=self.show_locals, variable=self.vlocals)
|
||||
self.blocals.grid(row=1, column=0)
|
||||
if not self.vglobals:
|
||||
self.__class__.vglobals = BooleanVar(top)
|
||||
##self.vglobals.set(1)
|
||||
self.bglobals = Checkbutton(cframe,
|
||||
text="Globals", command=self.show_globals, variable=self.vglobals)
|
||||
self.bglobals.grid(row=1, column=1)
|
||||
#
|
||||
self.status = Label(top, anchor="w")
|
||||
self.status.pack(anchor="w")
|
||||
self.error = Label(top, anchor="w")
|
||||
self.error.pack(anchor="w", fill="x")
|
||||
self.errorbg = self.error.cget("background")
|
||||
#
|
||||
self.fstack = Frame(top, height=1)
|
||||
self.fstack.pack(expand=1, fill="both")
|
||||
self.flocals = Frame(top)
|
||||
self.flocals.pack(expand=1, fill="both")
|
||||
self.fglobals = Frame(top, height=1)
|
||||
self.fglobals.pack(expand=1, fill="both")
|
||||
#
|
||||
if self.vstack.get():
|
||||
self.show_stack()
|
||||
if self.vlocals.get():
|
||||
self.show_locals()
|
||||
if self.vglobals.get():
|
||||
self.show_globals()
|
||||
|
||||
frame = None
|
||||
|
||||
def interaction(self, frame, info=None):
|
||||
self.frame = frame
|
||||
code = frame.f_code
|
||||
file = code.co_filename
|
||||
base = os.path.basename(file)
|
||||
lineno = frame.f_lineno
|
||||
#
|
||||
message = "%s:%s" % (base, lineno)
|
||||
if code.co_name != "?":
|
||||
message = "%s: %s()" % (message, code.co_name)
|
||||
self.status.configure(text=message)
|
||||
#
|
||||
if info:
|
||||
type, value, tb = info
|
||||
try:
|
||||
m1 = type.__name__
|
||||
except AttributeError:
|
||||
m1 = "%s" % str(type)
|
||||
if value is not None:
|
||||
try:
|
||||
m1 = "%s: %s" % (m1, str(value))
|
||||
except:
|
||||
pass
|
||||
bg = "yellow"
|
||||
else:
|
||||
m1 = ""
|
||||
tb = None
|
||||
bg = self.errorbg
|
||||
self.error.configure(text=m1, background=bg)
|
||||
#
|
||||
sv = self.stackviewer
|
||||
if sv:
|
||||
stack, i = self.get_stack(self.frame, tb)
|
||||
sv.load_stack(stack, i)
|
||||
#
|
||||
self.show_variables(1)
|
||||
#
|
||||
if self.vsource.get():
|
||||
self.sync_source_line()
|
||||
#
|
||||
for b in self.buttons:
|
||||
b.configure(state="normal")
|
||||
#
|
||||
self.top.tkraise()
|
||||
self.root.mainloop()
|
||||
#
|
||||
for b in self.buttons:
|
||||
b.configure(state="disabled")
|
||||
self.status.configure(text="")
|
||||
self.error.configure(text="", background=self.errorbg)
|
||||
self.frame = None
|
||||
|
||||
def sync_source_line(self):
|
||||
frame = self.frame
|
||||
if not frame:
|
||||
return
|
||||
code = frame.f_code
|
||||
file = code.co_filename
|
||||
lineno = frame.f_lineno
|
||||
if file[:1] + file[-1:] != "<>" and os.path.exists(file):
|
||||
edit = self.flist.open(file)
|
||||
if edit:
|
||||
edit.gotoline(lineno)
|
||||
|
||||
def cont(self):
|
||||
self.set_continue()
|
||||
self.root.quit()
|
||||
|
||||
def step(self):
|
||||
self.set_step()
|
||||
self.root.quit()
|
||||
|
||||
def next(self):
|
||||
self.set_next(self.frame)
|
||||
self.root.quit()
|
||||
|
||||
def ret(self):
|
||||
self.set_return(self.frame)
|
||||
self.root.quit()
|
||||
|
||||
def quit(self):
|
||||
self.set_quit()
|
||||
self.root.quit()
|
||||
|
||||
stackviewer = None
|
||||
|
||||
def show_stack(self):
|
||||
if not self.stackviewer and self.vstack.get():
|
||||
self.stackviewer = sv = StackViewer.StackViewer(
|
||||
self.fstack, self.flist, self)
|
||||
if self.frame:
|
||||
stack, i = self.get_stack(self.frame, None)
|
||||
sv.load_stack(stack, i)
|
||||
else:
|
||||
sv = self.stackviewer
|
||||
if sv and not self.vstack.get():
|
||||
self.stackviewer = None
|
||||
sv.close()
|
||||
self.fstack['height'] = 1
|
||||
|
||||
def show_source(self):
|
||||
if self.vsource.get():
|
||||
self.sync_source_line()
|
||||
|
||||
def show_frame(self, (frame, lineno)):
|
||||
self.frame = frame
|
||||
self.show_variables()
|
||||
|
||||
localsviewer = None
|
||||
globalsviewer = None
|
||||
|
||||
def show_locals(self):
|
||||
lv = self.localsviewer
|
||||
if self.vlocals.get():
|
||||
if not lv:
|
||||
self.localsviewer = StackViewer.NamespaceViewer(
|
||||
self.flocals, "Locals")
|
||||
else:
|
||||
if lv:
|
||||
self.localsviewer = None
|
||||
lv.close()
|
||||
self.flocals['height'] = 1
|
||||
self.show_variables()
|
||||
|
||||
def show_globals(self):
|
||||
gv = self.globalsviewer
|
||||
if self.vglobals.get():
|
||||
if not gv:
|
||||
self.globalsviewer = StackViewer.NamespaceViewer(
|
||||
self.fglobals, "Globals")
|
||||
else:
|
||||
if gv:
|
||||
self.globalsviewer = None
|
||||
gv.close()
|
||||
self.fglobals['height'] = 1
|
||||
self.show_variables()
|
||||
|
||||
def show_variables(self, force=0):
|
||||
lv = self.localsviewer
|
||||
gv = self.globalsviewer
|
||||
frame = self.frame
|
||||
if not frame:
|
||||
ldict = gdict = None
|
||||
else:
|
||||
ldict = frame.f_locals
|
||||
gdict = frame.f_globals
|
||||
if lv and gv and ldict is gdict:
|
||||
ldict = None
|
||||
if lv:
|
||||
lv.load_dict(ldict, force)
|
||||
if gv:
|
||||
gv.load_dict(gdict, force)
|
||||
|
||||
def set_breakpoint_here(self, edit):
|
||||
text = edit.text
|
||||
filename = edit.io.filename
|
||||
if not filename:
|
||||
text.bell()
|
||||
return
|
||||
lineno = int(float(text.index("insert")))
|
||||
msg = self.set_break(filename, lineno)
|
||||
if msg:
|
||||
text.bell()
|
||||
return
|
||||
text.tag_add("BREAK", "insert linestart", "insert lineend +1char")
|
||||
|
||||
# A literal copy of Bdb.set_break() without the print statement at the end
|
||||
def set_break(self, filename, lineno, temporary=0, cond = None):
|
||||
import linecache # Import as late as possible
|
||||
filename = self.canonic(filename)
|
||||
line = linecache.getline(filename, lineno)
|
||||
if not line:
|
||||
return 'That line does not exist!'
|
||||
if not self.breaks.has_key(filename):
|
||||
self.breaks[filename] = []
|
||||
list = self.breaks[filename]
|
||||
if not lineno in list:
|
||||
list.append(lineno)
|
||||
bp = bdb.Breakpoint(filename, lineno, temporary, cond)
|
|
@ -1,33 +0,0 @@
|
|||
class Delegator:
|
||||
|
||||
# The cache is only used to be able to change delegates!
|
||||
|
||||
def __init__(self, delegate=None):
|
||||
self.delegate = delegate
|
||||
self.__cache = {}
|
||||
|
||||
def __getattr__(self, name):
|
||||
attr = getattr(self.delegate, name) # May raise AttributeError
|
||||
setattr(self, name, attr)
|
||||
self.__cache[name] = attr
|
||||
return attr
|
||||
|
||||
def resetcache(self):
|
||||
for key in self.__cache.keys():
|
||||
try:
|
||||
delattr(self, key)
|
||||
except AttributeError:
|
||||
pass
|
||||
self.__cache.clear()
|
||||
|
||||
def cachereport(self):
|
||||
keys = self.__cache.keys()
|
||||
keys.sort()
|
||||
print keys
|
||||
|
||||
def setdelegate(self, delegate):
|
||||
self.resetcache()
|
||||
self.delegate = delegate
|
||||
|
||||
def getdelegate(self):
|
||||
return self.delegate
|
|
@ -1,756 +0,0 @@
|
|||
import sys
|
||||
import os
|
||||
import re
|
||||
import imp
|
||||
from Tkinter import *
|
||||
import tkSimpleDialog
|
||||
import tkMessageBox
|
||||
|
||||
import webbrowser
|
||||
import idlever
|
||||
import WindowList
|
||||
from IdleConf import idleconf
|
||||
|
||||
# The default tab setting for a Text widget, in average-width characters.
|
||||
TK_TABWIDTH_DEFAULT = 8
|
||||
|
||||
# File menu
|
||||
|
||||
#$ event <<open-module>>
|
||||
#$ win <Alt-m>
|
||||
#$ unix <Control-x><Control-m>
|
||||
|
||||
#$ event <<open-class-browser>>
|
||||
#$ win <Alt-c>
|
||||
#$ unix <Control-x><Control-b>
|
||||
|
||||
#$ event <<open-path-browser>>
|
||||
|
||||
#$ event <<close-window>>
|
||||
|
||||
#$ unix <Control-x><Control-0>
|
||||
#$ unix <Control-x><Key-0>
|
||||
#$ win <Alt-F4>
|
||||
|
||||
# Edit menu
|
||||
|
||||
#$ event <<Copy>>
|
||||
#$ win <Control-c>
|
||||
#$ unix <Alt-w>
|
||||
|
||||
#$ event <<Cut>>
|
||||
#$ win <Control-x>
|
||||
#$ unix <Control-w>
|
||||
|
||||
#$ event <<Paste>>
|
||||
#$ win <Control-v>
|
||||
#$ unix <Control-y>
|
||||
|
||||
#$ event <<select-all>>
|
||||
#$ win <Alt-a>
|
||||
#$ unix <Alt-a>
|
||||
|
||||
# Help menu
|
||||
|
||||
#$ event <<help>>
|
||||
#$ win <F1>
|
||||
#$ unix <F1>
|
||||
|
||||
#$ event <<about-idle>>
|
||||
|
||||
# Events without menu entries
|
||||
|
||||
#$ event <<remove-selection>>
|
||||
#$ win <Escape>
|
||||
|
||||
#$ event <<center-insert>>
|
||||
#$ win <Control-l>
|
||||
#$ unix <Control-l>
|
||||
|
||||
#$ event <<do-nothing>>
|
||||
#$ unix <Control-x>
|
||||
|
||||
|
||||
about_title = "About IDLE"
|
||||
about_text = """\
|
||||
IDLE %s
|
||||
|
||||
An Integrated DeveLopment Environment for Python
|
||||
|
||||
by Guido van Rossum
|
||||
""" % idlever.IDLE_VERSION
|
||||
|
||||
def _find_module(fullname, path=None):
|
||||
"""Version of imp.find_module() that handles hierarchical module names"""
|
||||
|
||||
file = None
|
||||
for tgt in fullname.split('.'):
|
||||
if file is not None:
|
||||
file.close() # close intermediate files
|
||||
(file, filename, descr) = imp.find_module(tgt, path)
|
||||
if descr[2] == imp.PY_SOURCE:
|
||||
break # find but not load the source file
|
||||
module = imp.load_module(tgt, file, filename, descr)
|
||||
try:
|
||||
path = module.__path__
|
||||
except AttributeError:
|
||||
raise ImportError, 'No source for module ' + module.__name__
|
||||
return file, filename, descr
|
||||
|
||||
class EditorWindow:
|
||||
|
||||
from Percolator import Percolator
|
||||
from ColorDelegator import ColorDelegator
|
||||
from UndoDelegator import UndoDelegator
|
||||
from IOBinding import IOBinding
|
||||
import Bindings
|
||||
from Tkinter import Toplevel
|
||||
from MultiStatusBar import MultiStatusBar
|
||||
|
||||
about_title = about_title
|
||||
about_text = about_text
|
||||
|
||||
vars = {}
|
||||
runnable = False # Shell window cannot Import Module or Run Script
|
||||
|
||||
def __init__(self, flist=None, filename=None, key=None, root=None):
|
||||
edconf = idleconf.getsection('EditorWindow')
|
||||
coconf = idleconf.getsection('Colors')
|
||||
self.flist = flist
|
||||
root = root or flist.root
|
||||
self.root = root
|
||||
if flist:
|
||||
self.vars = flist.vars
|
||||
self.menubar = Menu(root)
|
||||
self.top = top = self.Toplevel(root, menu=self.menubar)
|
||||
self.vbar = vbar = Scrollbar(top, name='vbar')
|
||||
self.text_frame = text_frame = Frame(top)
|
||||
self.text = text = Text(text_frame, name='text', padx=5,
|
||||
foreground=coconf.getdef('normal-foreground'),
|
||||
background=coconf.getdef('normal-background'),
|
||||
highlightcolor=coconf.getdef('hilite-foreground'),
|
||||
highlightbackground=coconf.getdef('hilite-background'),
|
||||
insertbackground=coconf.getdef('cursor-background'),
|
||||
width=edconf.getint('width'),
|
||||
height=edconf.getint('height'),
|
||||
wrap="none")
|
||||
|
||||
self.createmenubar()
|
||||
self.apply_bindings()
|
||||
|
||||
self.top.protocol("WM_DELETE_WINDOW", self.close)
|
||||
self.top.bind("<<close-window>>", self.close_event)
|
||||
text.bind("<<center-insert>>", self.center_insert_event)
|
||||
text.bind("<<help>>", self.help_dialog)
|
||||
text.bind("<<python-docs>>", self.python_docs)
|
||||
text.bind("<<about-idle>>", self.about_dialog)
|
||||
text.bind("<<open-module>>", self.open_module)
|
||||
text.bind("<<do-nothing>>", lambda event: "break")
|
||||
text.bind("<<select-all>>", self.select_all)
|
||||
text.bind("<<remove-selection>>", self.remove_selection)
|
||||
text.bind("<3>", self.right_menu_event)
|
||||
if flist:
|
||||
flist.inversedict[self] = key
|
||||
if key:
|
||||
flist.dict[key] = self
|
||||
text.bind("<<open-new-window>>", self.flist.new_callback)
|
||||
text.bind("<<close-all-windows>>", self.flist.close_all_callback)
|
||||
text.bind("<<open-class-browser>>", self.open_class_browser)
|
||||
text.bind("<<open-path-browser>>", self.open_path_browser)
|
||||
|
||||
vbar['command'] = text.yview
|
||||
vbar.pack(side=RIGHT, fill=Y)
|
||||
|
||||
text['yscrollcommand'] = vbar.set
|
||||
text['font'] = edconf.get('font-name'), edconf.get('font-size')
|
||||
text_frame.pack(side=LEFT, fill=BOTH, expand=1)
|
||||
text.pack(side=TOP, fill=BOTH, expand=1)
|
||||
text.focus_set()
|
||||
|
||||
self.per = per = self.Percolator(text)
|
||||
if self.ispythonsource(filename):
|
||||
self.color = color = self.ColorDelegator(); per.insertfilter(color)
|
||||
##print "Initial colorizer"
|
||||
else:
|
||||
##print "No initial colorizer"
|
||||
self.color = None
|
||||
self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
|
||||
self.io = io = self.IOBinding(self)
|
||||
|
||||
text.undo_block_start = undo.undo_block_start
|
||||
text.undo_block_stop = undo.undo_block_stop
|
||||
undo.set_saved_change_hook(self.saved_change_hook)
|
||||
io.set_filename_change_hook(self.filename_change_hook)
|
||||
|
||||
if filename:
|
||||
if os.path.exists(filename):
|
||||
io.loadfile(filename)
|
||||
else:
|
||||
io.set_filename(filename)
|
||||
|
||||
self.saved_change_hook()
|
||||
|
||||
self.load_extensions()
|
||||
|
||||
menu = self.menudict.get('windows')
|
||||
if menu:
|
||||
end = menu.index("end")
|
||||
if end is None:
|
||||
end = -1
|
||||
if end >= 0:
|
||||
menu.add_separator()
|
||||
end = end + 1
|
||||
self.wmenu_end = end
|
||||
WindowList.register_callback(self.postwindowsmenu)
|
||||
|
||||
# Some abstractions so IDLE extensions are cross-IDE
|
||||
self.askyesno = tkMessageBox.askyesno
|
||||
self.askinteger = tkSimpleDialog.askinteger
|
||||
self.showerror = tkMessageBox.showerror
|
||||
|
||||
if self.extensions.has_key('AutoIndent'):
|
||||
self.extensions['AutoIndent'].set_indentation_params(
|
||||
self.ispythonsource(filename))
|
||||
self.set_status_bar()
|
||||
|
||||
def set_status_bar(self):
|
||||
self.status_bar = self.MultiStatusBar(self.text_frame)
|
||||
self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
|
||||
self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
|
||||
self.status_bar.pack(side=BOTTOM, fill=X)
|
||||
self.text.bind('<KeyRelease>', self.set_line_and_column)
|
||||
self.text.bind('<ButtonRelease>', self.set_line_and_column)
|
||||
self.text.after_idle(self.set_line_and_column)
|
||||
|
||||
def set_line_and_column(self, event=None):
|
||||
line, column = self.text.index(INSERT).split('.')
|
||||
self.status_bar.set_label('column', 'Col: %s' % column)
|
||||
self.status_bar.set_label('line', 'Ln: %s' % line)
|
||||
|
||||
def wakeup(self):
|
||||
if self.top.wm_state() == "iconic":
|
||||
self.top.wm_deiconify()
|
||||
else:
|
||||
self.top.tkraise()
|
||||
self.text.focus_set()
|
||||
|
||||
menu_specs = [
|
||||
("file", "_File"),
|
||||
("edit", "_Edit"),
|
||||
("windows", "_Windows"),
|
||||
("help", "_Help"),
|
||||
]
|
||||
|
||||
def createmenubar(self):
|
||||
mbar = self.menubar
|
||||
self.menudict = menudict = {}
|
||||
for name, label in self.menu_specs:
|
||||
underline, label = prepstr(label)
|
||||
menudict[name] = menu = Menu(mbar, name=name)
|
||||
mbar.add_cascade(label=label, menu=menu, underline=underline)
|
||||
self.fill_menus()
|
||||
|
||||
def postwindowsmenu(self):
|
||||
# Only called when Windows menu exists
|
||||
# XXX Actually, this Just-In-Time updating interferes badly
|
||||
# XXX with the tear-off feature. It would be better to update
|
||||
# XXX all Windows menus whenever the list of windows changes.
|
||||
menu = self.menudict['windows']
|
||||
end = menu.index("end")
|
||||
if end is None:
|
||||
end = -1
|
||||
if end > self.wmenu_end:
|
||||
menu.delete(self.wmenu_end+1, end)
|
||||
WindowList.add_windows_to_menu(menu)
|
||||
|
||||
rmenu = None
|
||||
|
||||
def right_menu_event(self, event):
|
||||
self.text.tag_remove("sel", "1.0", "end")
|
||||
self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
|
||||
if not self.rmenu:
|
||||
self.make_rmenu()
|
||||
rmenu = self.rmenu
|
||||
self.event = event
|
||||
iswin = sys.platform[:3] == 'win'
|
||||
if iswin:
|
||||
self.text.config(cursor="arrow")
|
||||
rmenu.tk_popup(event.x_root, event.y_root)
|
||||
if iswin:
|
||||
self.text.config(cursor="ibeam")
|
||||
|
||||
rmenu_specs = [
|
||||
# ("Label", "<<virtual-event>>"), ...
|
||||
("Close", "<<close-window>>"), # Example
|
||||
]
|
||||
|
||||
def make_rmenu(self):
|
||||
rmenu = Menu(self.text, tearoff=0)
|
||||
for label, eventname in self.rmenu_specs:
|
||||
def command(text=self.text, eventname=eventname):
|
||||
text.event_generate(eventname)
|
||||
rmenu.add_command(label=label, command=command)
|
||||
self.rmenu = rmenu
|
||||
|
||||
def about_dialog(self, event=None):
|
||||
tkMessageBox.showinfo(self.about_title, self.about_text,
|
||||
master=self.text)
|
||||
|
||||
helpfile = "help.txt"
|
||||
|
||||
def help_dialog(self, event=None):
|
||||
try:
|
||||
helpfile = os.path.join(os.path.dirname(__file__), self.helpfile)
|
||||
except NameError:
|
||||
helpfile = self.helpfile
|
||||
if self.flist:
|
||||
self.flist.open(helpfile)
|
||||
else:
|
||||
self.io.loadfile(helpfile)
|
||||
|
||||
help_url = "http://www.python.org/doc/current/"
|
||||
if sys.platform[:3] == "win":
|
||||
fn = os.path.dirname(__file__)
|
||||
fn = os.path.join(fn, os.pardir, os.pardir, "pythlp.chm")
|
||||
fn = os.path.normpath(fn)
|
||||
if os.path.isfile(fn):
|
||||
help_url = fn
|
||||
else:
|
||||
fn = os.path.dirname(__file__)
|
||||
fn = os.path.join(fn, os.pardir, os.pardir, "Doc", "index.html")
|
||||
fn = os.path.normpath(fn)
|
||||
if os.path.isfile(fn):
|
||||
help_url = fn
|
||||
del fn
|
||||
|
||||
def python_docs(self, event=None):
|
||||
os.startfile(self.help_url)
|
||||
else:
|
||||
def python_docs(self, event=None):
|
||||
webbrowser.open(self.help_url)
|
||||
|
||||
def select_all(self, event=None):
|
||||
self.text.tag_add("sel", "1.0", "end-1c")
|
||||
self.text.mark_set("insert", "1.0")
|
||||
self.text.see("insert")
|
||||
return "break"
|
||||
|
||||
def remove_selection(self, event=None):
|
||||
self.text.tag_remove("sel", "1.0", "end")
|
||||
self.text.see("insert")
|
||||
|
||||
def open_module(self, event=None):
|
||||
# XXX Shouldn't this be in IOBinding or in FileList?
|
||||
try:
|
||||
name = self.text.get("sel.first", "sel.last")
|
||||
except TclError:
|
||||
name = ""
|
||||
else:
|
||||
name = name.strip()
|
||||
if not name:
|
||||
name = tkSimpleDialog.askstring("Module",
|
||||
"Enter the name of a Python module\n"
|
||||
"to search on sys.path and open:",
|
||||
parent=self.text)
|
||||
if name:
|
||||
name = name.strip()
|
||||
if not name:
|
||||
return
|
||||
# XXX Ought to insert current file's directory in front of path
|
||||
try:
|
||||
(f, file, (suffix, mode, type)) = _find_module(name)
|
||||
except (NameError, ImportError), msg:
|
||||
tkMessageBox.showerror("Import error", str(msg), parent=self.text)
|
||||
return
|
||||
if type != imp.PY_SOURCE:
|
||||
tkMessageBox.showerror("Unsupported type",
|
||||
"%s is not a source module" % name, parent=self.text)
|
||||
return
|
||||
if f:
|
||||
f.close()
|
||||
if self.flist:
|
||||
self.flist.open(file)
|
||||
else:
|
||||
self.io.loadfile(file)
|
||||
|
||||
def open_class_browser(self, event=None):
|
||||
filename = self.io.filename
|
||||
if not filename:
|
||||
tkMessageBox.showerror(
|
||||
"No filename",
|
||||
"This buffer has no associated filename",
|
||||
master=self.text)
|
||||
self.text.focus_set()
|
||||
return None
|
||||
head, tail = os.path.split(filename)
|
||||
base, ext = os.path.splitext(tail)
|
||||
import ClassBrowser
|
||||
ClassBrowser.ClassBrowser(self.flist, base, [head])
|
||||
|
||||
def open_path_browser(self, event=None):
|
||||
import PathBrowser
|
||||
PathBrowser.PathBrowser(self.flist)
|
||||
|
||||
def gotoline(self, lineno):
|
||||
if lineno is not None and lineno > 0:
|
||||
self.text.mark_set("insert", "%d.0" % lineno)
|
||||
self.text.tag_remove("sel", "1.0", "end")
|
||||
self.text.tag_add("sel", "insert", "insert +1l")
|
||||
self.center()
|
||||
|
||||
def ispythonsource(self, filename):
|
||||
if not filename:
|
||||
return True
|
||||
base, ext = os.path.splitext(os.path.basename(filename))
|
||||
if os.path.normcase(ext) in (".py", ".pyw"):
|
||||
return True
|
||||
try:
|
||||
f = open(filename)
|
||||
line = f.readline()
|
||||
f.close()
|
||||
except IOError:
|
||||
return False
|
||||
return line.startswith('#!') and 'python' in line
|
||||
|
||||
def close_hook(self):
|
||||
if self.flist:
|
||||
self.flist.close_edit(self)
|
||||
|
||||
def set_close_hook(self, close_hook):
|
||||
self.close_hook = close_hook
|
||||
|
||||
def filename_change_hook(self):
|
||||
if self.flist:
|
||||
self.flist.filename_changed_edit(self)
|
||||
self.saved_change_hook()
|
||||
if self.ispythonsource(self.io.filename):
|
||||
self.addcolorizer()
|
||||
else:
|
||||
self.rmcolorizer()
|
||||
|
||||
def addcolorizer(self):
|
||||
if self.color:
|
||||
return
|
||||
##print "Add colorizer"
|
||||
self.per.removefilter(self.undo)
|
||||
self.color = self.ColorDelegator()
|
||||
self.per.insertfilter(self.color)
|
||||
self.per.insertfilter(self.undo)
|
||||
|
||||
def rmcolorizer(self):
|
||||
if not self.color:
|
||||
return
|
||||
##print "Remove colorizer"
|
||||
self.per.removefilter(self.undo)
|
||||
self.per.removefilter(self.color)
|
||||
self.color = None
|
||||
self.per.insertfilter(self.undo)
|
||||
|
||||
def saved_change_hook(self):
|
||||
short = self.short_title()
|
||||
long = self.long_title()
|
||||
if short and long:
|
||||
title = short + " - " + long
|
||||
elif short:
|
||||
title = short
|
||||
elif long:
|
||||
title = long
|
||||
else:
|
||||
title = "Untitled"
|
||||
icon = short or long or title
|
||||
if not self.get_saved():
|
||||
title = "*%s*" % title
|
||||
icon = "*%s" % icon
|
||||
self.top.wm_title(title)
|
||||
self.top.wm_iconname(icon)
|
||||
|
||||
def get_saved(self):
|
||||
return self.undo.get_saved()
|
||||
|
||||
def set_saved(self, flag):
|
||||
self.undo.set_saved(flag)
|
||||
|
||||
def reset_undo(self):
|
||||
self.undo.reset_undo()
|
||||
|
||||
def short_title(self):
|
||||
filename = self.io.filename
|
||||
if filename:
|
||||
filename = os.path.basename(filename)
|
||||
return filename
|
||||
|
||||
def long_title(self):
|
||||
return self.io.filename or ""
|
||||
|
||||
def center_insert_event(self, event):
|
||||
self.center()
|
||||
|
||||
def center(self, mark="insert"):
|
||||
text = self.text
|
||||
top, bot = self.getwindowlines()
|
||||
lineno = self.getlineno(mark)
|
||||
height = bot - top
|
||||
newtop = max(1, lineno - height//2)
|
||||
text.yview(float(newtop))
|
||||
|
||||
def getwindowlines(self):
|
||||
text = self.text
|
||||
top = self.getlineno("@0,0")
|
||||
bot = self.getlineno("@0,65535")
|
||||
if top == bot and text.winfo_height() == 1:
|
||||
# Geometry manager hasn't run yet
|
||||
height = int(text['height'])
|
||||
bot = top + height - 1
|
||||
return top, bot
|
||||
|
||||
def getlineno(self, mark="insert"):
|
||||
text = self.text
|
||||
return int(float(text.index(mark)))
|
||||
|
||||
def close_event(self, event):
|
||||
self.close()
|
||||
|
||||
def maybesave(self):
|
||||
if self.io:
|
||||
return self.io.maybesave()
|
||||
|
||||
def close(self):
|
||||
self.top.wm_deiconify()
|
||||
self.top.tkraise()
|
||||
reply = self.maybesave()
|
||||
if reply != "cancel":
|
||||
self._close()
|
||||
return reply
|
||||
|
||||
def _close(self):
|
||||
WindowList.unregister_callback(self.postwindowsmenu)
|
||||
if self.close_hook:
|
||||
self.close_hook()
|
||||
self.flist = None
|
||||
colorizing = 0
|
||||
self.unload_extensions()
|
||||
self.io.close(); self.io = None
|
||||
self.undo = None # XXX
|
||||
if self.color:
|
||||
colorizing = self.color.colorizing
|
||||
doh = colorizing and self.top
|
||||
self.color.close(doh) # Cancel colorization
|
||||
self.text = None
|
||||
self.vars = None
|
||||
self.per.close(); self.per = None
|
||||
if not colorizing:
|
||||
self.top.destroy()
|
||||
|
||||
def load_extensions(self):
|
||||
self.extensions = {}
|
||||
self.load_standard_extensions()
|
||||
|
||||
def unload_extensions(self):
|
||||
for ins in self.extensions.values():
|
||||
if hasattr(ins, "close"):
|
||||
ins.close()
|
||||
self.extensions = {}
|
||||
|
||||
def load_standard_extensions(self):
|
||||
for name in self.get_standard_extension_names():
|
||||
try:
|
||||
self.load_extension(name)
|
||||
except:
|
||||
print "Failed to load extension", `name`
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def get_standard_extension_names(self):
|
||||
return idleconf.getextensions()
|
||||
|
||||
def load_extension(self, name):
|
||||
mod = __import__(name, globals(), locals(), [])
|
||||
cls = getattr(mod, name)
|
||||
ins = cls(self)
|
||||
self.extensions[name] = ins
|
||||
kdnames = ["keydefs"]
|
||||
if sys.platform == 'win32':
|
||||
kdnames.append("windows_keydefs")
|
||||
elif sys.platform == 'mac':
|
||||
kdnames.append("mac_keydefs")
|
||||
else:
|
||||
kdnames.append("unix_keydefs")
|
||||
keydefs = {}
|
||||
for kdname in kdnames:
|
||||
if hasattr(ins, kdname):
|
||||
keydefs.update(getattr(ins, kdname))
|
||||
if keydefs:
|
||||
self.apply_bindings(keydefs)
|
||||
for vevent in keydefs.keys():
|
||||
methodname = vevent.replace("-", "_")
|
||||
while methodname[:1] == '<':
|
||||
methodname = methodname[1:]
|
||||
while methodname[-1:] == '>':
|
||||
methodname = methodname[:-1]
|
||||
methodname = methodname + "_event"
|
||||
if hasattr(ins, methodname):
|
||||
self.text.bind(vevent, getattr(ins, methodname))
|
||||
if hasattr(ins, "menudefs"):
|
||||
self.fill_menus(ins.menudefs, keydefs)
|
||||
return ins
|
||||
|
||||
def apply_bindings(self, keydefs=None):
|
||||
if keydefs is None:
|
||||
keydefs = self.Bindings.default_keydefs
|
||||
text = self.text
|
||||
text.keydefs = keydefs
|
||||
for event, keylist in keydefs.items():
|
||||
if keylist:
|
||||
apply(text.event_add, (event,) + tuple(keylist))
|
||||
|
||||
def fill_menus(self, defs=None, keydefs=None):
|
||||
# Fill the menus. Menus that are absent or None in
|
||||
# self.menudict are ignored.
|
||||
if defs is None:
|
||||
defs = self.Bindings.menudefs
|
||||
if keydefs is None:
|
||||
keydefs = self.Bindings.default_keydefs
|
||||
menudict = self.menudict
|
||||
text = self.text
|
||||
for mname, itemlist in defs:
|
||||
menu = menudict.get(mname)
|
||||
if not menu:
|
||||
continue
|
||||
for item in itemlist:
|
||||
if not item:
|
||||
menu.add_separator()
|
||||
else:
|
||||
label, event = item
|
||||
checkbutton = (label[:1] == '!')
|
||||
if checkbutton:
|
||||
label = label[1:]
|
||||
underline, label = prepstr(label)
|
||||
accelerator = get_accelerator(keydefs, event)
|
||||
def command(text=text, event=event):
|
||||
text.event_generate(event)
|
||||
if checkbutton:
|
||||
var = self.getrawvar(event, BooleanVar)
|
||||
menu.add_checkbutton(label=label, underline=underline,
|
||||
command=command, accelerator=accelerator,
|
||||
variable=var)
|
||||
else:
|
||||
menu.add_command(label=label, underline=underline,
|
||||
command=command, accelerator=accelerator)
|
||||
|
||||
def getvar(self, name):
|
||||
var = self.getrawvar(name)
|
||||
if var:
|
||||
return var.get()
|
||||
|
||||
def setvar(self, name, value, vartype=None):
|
||||
var = self.getrawvar(name, vartype)
|
||||
if var:
|
||||
var.set(value)
|
||||
|
||||
def getrawvar(self, name, vartype=None):
|
||||
var = self.vars.get(name)
|
||||
if not var and vartype:
|
||||
self.vars[name] = var = vartype(self.text)
|
||||
return var
|
||||
|
||||
# Tk implementations of "virtual text methods" -- each platform
|
||||
# reusing IDLE's support code needs to define these for its GUI's
|
||||
# flavor of widget.
|
||||
|
||||
# Is character at text_index in a Python string? Return 0 for
|
||||
# "guaranteed no", true for anything else. This info is expensive
|
||||
# to compute ab initio, but is probably already known by the
|
||||
# platform's colorizer.
|
||||
|
||||
def is_char_in_string(self, text_index):
|
||||
if self.color:
|
||||
# Return true iff colorizer hasn't (re)gotten this far
|
||||
# yet, or the character is tagged as being in a string
|
||||
return self.text.tag_prevrange("TODO", text_index) or \
|
||||
"STRING" in self.text.tag_names(text_index)
|
||||
else:
|
||||
# The colorizer is missing: assume the worst
|
||||
return 1
|
||||
|
||||
# If a selection is defined in the text widget, return (start,
|
||||
# end) as Tkinter text indices, otherwise return (None, None)
|
||||
def get_selection_indices(self):
|
||||
try:
|
||||
first = self.text.index("sel.first")
|
||||
last = self.text.index("sel.last")
|
||||
return first, last
|
||||
except TclError:
|
||||
return None, None
|
||||
|
||||
# Return the text widget's current view of what a tab stop means
|
||||
# (equivalent width in spaces).
|
||||
|
||||
def get_tabwidth(self):
|
||||
current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
|
||||
return int(current)
|
||||
|
||||
# Set the text widget's current view of what a tab stop means.
|
||||
|
||||
def set_tabwidth(self, newtabwidth):
|
||||
text = self.text
|
||||
if self.get_tabwidth() != newtabwidth:
|
||||
pixels = text.tk.call("font", "measure", text["font"],
|
||||
"-displayof", text.master,
|
||||
"n" * newtabwidth)
|
||||
text.configure(tabs=pixels)
|
||||
|
||||
def prepstr(s):
|
||||
# Helper to extract the underscore from a string, e.g.
|
||||
# prepstr("Co_py") returns (2, "Copy").
|
||||
i = s.find('_')
|
||||
if i >= 0:
|
||||
s = s[:i] + s[i+1:]
|
||||
return i, s
|
||||
|
||||
|
||||
keynames = {
|
||||
'bracketleft': '[',
|
||||
'bracketright': ']',
|
||||
'slash': '/',
|
||||
}
|
||||
|
||||
def get_accelerator(keydefs, event):
|
||||
keylist = keydefs.get(event)
|
||||
if not keylist:
|
||||
return ""
|
||||
s = keylist[0]
|
||||
s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s)
|
||||
s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
|
||||
s = re.sub("Key-", "", s)
|
||||
s = re.sub("Control-", "Ctrl-", s)
|
||||
s = re.sub("-", "+", s)
|
||||
s = re.sub("><", " ", s)
|
||||
s = re.sub("<", "", s)
|
||||
s = re.sub(">", "", s)
|
||||
return s
|
||||
|
||||
|
||||
def fixwordbreaks(root):
|
||||
# Make sure that Tk's double-click and next/previous word
|
||||
# operations use our definition of a word (i.e. an identifier)
|
||||
tk = root.tk
|
||||
tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
|
||||
tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
|
||||
tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
|
||||
|
||||
|
||||
def test():
|
||||
root = Tk()
|
||||
fixwordbreaks(root)
|
||||
root.withdraw()
|
||||
if sys.argv[1:]:
|
||||
filename = sys.argv[1]
|
||||
else:
|
||||
filename = None
|
||||
edit = EditorWindow(root=root, filename=filename)
|
||||
edit.set_close_hook(root.quit)
|
||||
root.mainloop()
|
||||
root.destroy()
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
|
@ -1,138 +0,0 @@
|
|||
import os
|
||||
from Tkinter import *
|
||||
import tkMessageBox
|
||||
|
||||
import WindowList
|
||||
|
||||
#$ event <<open-new-window>>
|
||||
#$ win <Control-n>
|
||||
#$ unix <Control-x><Control-n>
|
||||
|
||||
# (This is labeled as 'Exit'in the File menu)
|
||||
#$ event <<close-all-windows>>
|
||||
#$ win <Control-q>
|
||||
#$ unix <Control-x><Control-c>
|
||||
|
||||
class FileList:
|
||||
|
||||
from EditorWindow import EditorWindow
|
||||
EditorWindow.Toplevel = WindowList.ListedToplevel # XXX Patch it!
|
||||
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.dict = {}
|
||||
self.inversedict = {}
|
||||
self.vars = {} # For EditorWindow.getrawvar (shared Tcl variables)
|
||||
|
||||
def open(self, filename):
|
||||
assert filename
|
||||
filename = self.canonize(filename)
|
||||
if os.path.isdir(filename):
|
||||
tkMessageBox.showerror(
|
||||
"Is A Directory",
|
||||
"The path %s is a directory." % `filename`,
|
||||
master=self.root)
|
||||
return None
|
||||
key = os.path.normcase(filename)
|
||||
if self.dict.has_key(key):
|
||||
edit = self.dict[key]
|
||||
edit.wakeup()
|
||||
return edit
|
||||
if not os.path.exists(filename):
|
||||
tkMessageBox.showinfo(
|
||||
"New File",
|
||||
"Opening non-existent file %s" % `filename`,
|
||||
master=self.root)
|
||||
return self.EditorWindow(self, filename, key)
|
||||
|
||||
def gotofileline(self, filename, lineno=None):
|
||||
edit = self.open(filename)
|
||||
if edit is not None and lineno is not None:
|
||||
edit.gotoline(lineno)
|
||||
|
||||
def new(self):
|
||||
return self.EditorWindow(self)
|
||||
|
||||
def new_callback(self, event):
|
||||
self.new()
|
||||
return "break"
|
||||
|
||||
def close_all_callback(self, event):
|
||||
for edit in self.inversedict.keys():
|
||||
reply = edit.close()
|
||||
if reply == "cancel":
|
||||
break
|
||||
return "break"
|
||||
|
||||
def close_edit(self, edit):
|
||||
try:
|
||||
key = self.inversedict[edit]
|
||||
except KeyError:
|
||||
print "Don't know this EditorWindow object. (close)"
|
||||
return
|
||||
if key:
|
||||
del self.dict[key]
|
||||
del self.inversedict[edit]
|
||||
if not self.inversedict:
|
||||
self.root.quit()
|
||||
|
||||
def filename_changed_edit(self, edit):
|
||||
edit.saved_change_hook()
|
||||
try:
|
||||
key = self.inversedict[edit]
|
||||
except KeyError:
|
||||
print "Don't know this EditorWindow object. (rename)"
|
||||
return
|
||||
filename = edit.io.filename
|
||||
if not filename:
|
||||
if key:
|
||||
del self.dict[key]
|
||||
self.inversedict[edit] = None
|
||||
return
|
||||
filename = self.canonize(filename)
|
||||
newkey = os.path.normcase(filename)
|
||||
if newkey == key:
|
||||
return
|
||||
if self.dict.has_key(newkey):
|
||||
conflict = self.dict[newkey]
|
||||
self.inversedict[conflict] = None
|
||||
tkMessageBox.showerror(
|
||||
"Name Conflict",
|
||||
"You now have multiple edit windows open for %s" % `filename`,
|
||||
master=self.root)
|
||||
self.dict[newkey] = edit
|
||||
self.inversedict[edit] = newkey
|
||||
if key:
|
||||
try:
|
||||
del self.dict[key]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def canonize(self, filename):
|
||||
if not os.path.isabs(filename):
|
||||
try:
|
||||
pwd = os.getcwd()
|
||||
except os.error:
|
||||
pass
|
||||
else:
|
||||
filename = os.path.join(pwd, filename)
|
||||
return os.path.normpath(filename)
|
||||
|
||||
|
||||
def _test():
|
||||
from EditorWindow import fixwordbreaks
|
||||
import sys
|
||||
root = Tk()
|
||||
fixwordbreaks(root)
|
||||
root.withdraw()
|
||||
flist = FileList(root)
|
||||
if sys.argv[1:]:
|
||||
for filename in sys.argv[1:]:
|
||||
flist.open(filename)
|
||||
else:
|
||||
flist.new()
|
||||
if flist.inversedict:
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
_test()
|
|
@ -1,154 +0,0 @@
|
|||
# Extension to format a paragraph
|
||||
|
||||
# Does basic, standard text formatting, and also understands Python
|
||||
# comment blocks. Thus, for editing Python source code, this
|
||||
# extension is really only suitable for reformatting these comment
|
||||
# blocks or triple-quoted strings.
|
||||
|
||||
# Known problems with comment reformatting:
|
||||
# * If there is a selection marked, and the first line of the
|
||||
# selection is not complete, the block will probably not be detected
|
||||
# as comments, and will have the normal "text formatting" rules
|
||||
# applied.
|
||||
# * If a comment block has leading whitespace that mixes tabs and
|
||||
# spaces, they will not be considered part of the same block.
|
||||
# * Fancy comments, like this bulleted list, arent handled :-)
|
||||
|
||||
import re
|
||||
|
||||
class FormatParagraph:
|
||||
|
||||
menudefs = [
|
||||
('edit', [
|
||||
('Format Paragraph', '<<format-paragraph>>'),
|
||||
])
|
||||
]
|
||||
|
||||
keydefs = {
|
||||
'<<format-paragraph>>': ['<Alt-q>'],
|
||||
}
|
||||
|
||||
unix_keydefs = {
|
||||
'<<format-paragraph>>': ['<Meta-q>'],
|
||||
}
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
|
||||
def close(self):
|
||||
self.editwin = None
|
||||
|
||||
def format_paragraph_event(self, event):
|
||||
text = self.editwin.text
|
||||
first, last = self.editwin.get_selection_indices()
|
||||
if first and last:
|
||||
data = text.get(first, last)
|
||||
comment_header = ''
|
||||
else:
|
||||
first, last, comment_header, data = \
|
||||
find_paragraph(text, text.index("insert"))
|
||||
if comment_header:
|
||||
# Reformat the comment lines - convert to text sans header.
|
||||
lines = data.split("\n")
|
||||
lines = map(lambda st, l=len(comment_header): st[l:], lines)
|
||||
data = "\n".join(lines)
|
||||
# Reformat to 70 chars or a 20 char width, whichever is greater.
|
||||
format_width = max(70-len(comment_header), 20)
|
||||
newdata = reformat_paragraph(data, format_width)
|
||||
# re-split and re-insert the comment header.
|
||||
newdata = newdata.split("\n")
|
||||
# If the block ends in a \n, we dont want the comment
|
||||
# prefix inserted after it. (Im not sure it makes sense to
|
||||
# reformat a comment block that isnt made of complete
|
||||
# lines, but whatever!) Can't think of a clean soltution,
|
||||
# so we hack away
|
||||
block_suffix = ""
|
||||
if not newdata[-1]:
|
||||
block_suffix = "\n"
|
||||
newdata = newdata[:-1]
|
||||
builder = lambda item, prefix=comment_header: prefix+item
|
||||
newdata = '\n'.join(map(builder, newdata)) + block_suffix
|
||||
else:
|
||||
# Just a normal text format
|
||||
newdata = reformat_paragraph(data)
|
||||
text.tag_remove("sel", "1.0", "end")
|
||||
if newdata != data:
|
||||
text.mark_set("insert", first)
|
||||
text.undo_block_start()
|
||||
text.delete(first, last)
|
||||
text.insert(first, newdata)
|
||||
text.undo_block_stop()
|
||||
else:
|
||||
text.mark_set("insert", last)
|
||||
text.see("insert")
|
||||
|
||||
def find_paragraph(text, mark):
|
||||
lineno, col = map(int, mark.split("."))
|
||||
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
|
||||
while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line):
|
||||
lineno = lineno + 1
|
||||
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
|
||||
first_lineno = lineno
|
||||
comment_header = get_comment_header(line)
|
||||
comment_header_len = len(comment_header)
|
||||
while get_comment_header(line)==comment_header and \
|
||||
not is_all_white(line[comment_header_len:]):
|
||||
lineno = lineno + 1
|
||||
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
|
||||
last = "%d.0" % lineno
|
||||
# Search back to beginning of paragraph
|
||||
lineno = first_lineno - 1
|
||||
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
|
||||
while lineno > 0 and \
|
||||
get_comment_header(line)==comment_header and \
|
||||
not is_all_white(line[comment_header_len:]):
|
||||
lineno = lineno - 1
|
||||
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
|
||||
first = "%d.0" % (lineno+1)
|
||||
return first, last, comment_header, text.get(first, last)
|
||||
|
||||
def reformat_paragraph(data, limit=70):
|
||||
lines = data.split("\n")
|
||||
i = 0
|
||||
n = len(lines)
|
||||
while i < n and is_all_white(lines[i]):
|
||||
i = i+1
|
||||
if i >= n:
|
||||
return data
|
||||
indent1 = get_indent(lines[i])
|
||||
if i+1 < n and not is_all_white(lines[i+1]):
|
||||
indent2 = get_indent(lines[i+1])
|
||||
else:
|
||||
indent2 = indent1
|
||||
new = lines[:i]
|
||||
partial = indent1
|
||||
while i < n and not is_all_white(lines[i]):
|
||||
# XXX Should take double space after period (etc.) into account
|
||||
words = re.split("(\s+)", lines[i])
|
||||
for j in range(0, len(words), 2):
|
||||
word = words[j]
|
||||
if not word:
|
||||
continue # Can happen when line ends in whitespace
|
||||
if len((partial + word).expandtabs()) > limit and \
|
||||
partial != indent1:
|
||||
new.append(partial.rstrip())
|
||||
partial = indent2
|
||||
partial = partial + word + " "
|
||||
if j+1 < len(words) and words[j+1] != " ":
|
||||
partial = partial + " "
|
||||
i = i+1
|
||||
new.append(partial.rstrip())
|
||||
# XXX Should reformat remaining paragraphs as well
|
||||
new.extend(lines[i:])
|
||||
return "\n".join(new)
|
||||
|
||||
def is_all_white(line):
|
||||
return re.match(r"^\s*$", line) is not None
|
||||
|
||||
def get_indent(line):
|
||||
return re.match(r"^(\s*)", line).group()
|
||||
|
||||
def get_comment_header(line):
|
||||
m = re.match(r"^(\s*#*)", line)
|
||||
if m is None: return ""
|
||||
return m.group(1)
|
|
@ -1,38 +0,0 @@
|
|||
from repr import Repr
|
||||
from Tkinter import *
|
||||
|
||||
class FrameViewer:
|
||||
|
||||
def __init__(self, root, frame):
|
||||
self.root = root
|
||||
self.frame = frame
|
||||
self.top = Toplevel(self.root)
|
||||
self.repr = Repr()
|
||||
self.repr.maxstring = 60
|
||||
self.load_variables()
|
||||
|
||||
def load_variables(self):
|
||||
row = 0
|
||||
if self.frame.f_locals is not self.frame.f_globals:
|
||||
l = Label(self.top, text="Local Variables",
|
||||
borderwidth=2, relief="raised")
|
||||
l.grid(row=row, column=0, columnspan=2, sticky="ew")
|
||||
row = self.load_names(self.frame.f_locals, row+1)
|
||||
l = Label(self.top, text="Global Variables",
|
||||
borderwidth=2, relief="raised")
|
||||
l.grid(row=row, column=0, columnspan=2, sticky="ew")
|
||||
row = self.load_names(self.frame.f_globals, row+1)
|
||||
|
||||
def load_names(self, dict, row):
|
||||
names = dict.keys()
|
||||
names.sort()
|
||||
for name in names:
|
||||
value = dict[name]
|
||||
svalue = self.repr.repr(value)
|
||||
l = Label(self.top, text=name)
|
||||
l.grid(row=row, column=0, sticky="w")
|
||||
l = Entry(self.top, width=60, borderwidth=0)
|
||||
l.insert(0, svalue)
|
||||
l.grid(row=row, column=1, sticky="w")
|
||||
row = row+1
|
||||
return row
|
|
@ -1,132 +0,0 @@
|
|||
import os
|
||||
import fnmatch
|
||||
import sys
|
||||
from Tkinter import *
|
||||
import SearchEngine
|
||||
from SearchDialogBase import SearchDialogBase
|
||||
|
||||
def grep(text, io=None, flist=None):
|
||||
root = text._root()
|
||||
engine = SearchEngine.get(root)
|
||||
if not hasattr(engine, "_grepdialog"):
|
||||
engine._grepdialog = GrepDialog(root, engine, flist)
|
||||
dialog = engine._grepdialog
|
||||
dialog.open(io)
|
||||
|
||||
class GrepDialog(SearchDialogBase):
|
||||
|
||||
title = "Find in Files Dialog"
|
||||
icon = "Grep"
|
||||
needwrapbutton = 0
|
||||
|
||||
def __init__(self, root, engine, flist):
|
||||
SearchDialogBase.__init__(self, root, engine)
|
||||
self.flist = flist
|
||||
self.globvar = StringVar(root)
|
||||
self.recvar = BooleanVar(root)
|
||||
|
||||
def open(self, io=None):
|
||||
SearchDialogBase.open(self, None)
|
||||
if io:
|
||||
path = io.filename or ""
|
||||
else:
|
||||
path = ""
|
||||
dir, base = os.path.split(path)
|
||||
head, tail = os.path.splitext(base)
|
||||
if not tail:
|
||||
tail = ".py"
|
||||
self.globvar.set(os.path.join(dir, "*" + tail))
|
||||
|
||||
def create_entries(self):
|
||||
SearchDialogBase.create_entries(self)
|
||||
self.globent = self.make_entry("In files:", self.globvar)
|
||||
|
||||
def create_other_buttons(self):
|
||||
f = self.make_frame()
|
||||
|
||||
btn = Checkbutton(f, anchor="w",
|
||||
variable=self.recvar,
|
||||
text="Recurse down subdirectories")
|
||||
btn.pack(side="top", fill="both")
|
||||
btn.select()
|
||||
|
||||
def create_command_buttons(self):
|
||||
SearchDialogBase.create_command_buttons(self)
|
||||
self.make_button("Search Files", self.default_command, 1)
|
||||
|
||||
def default_command(self, event=None):
|
||||
prog = self.engine.getprog()
|
||||
if not prog:
|
||||
return
|
||||
path = self.globvar.get()
|
||||
if not path:
|
||||
self.top.bell()
|
||||
return
|
||||
from OutputWindow import OutputWindow
|
||||
save = sys.stdout
|
||||
try:
|
||||
sys.stdout = OutputWindow(self.flist)
|
||||
self.grep_it(prog, path)
|
||||
finally:
|
||||
sys.stdout = save
|
||||
|
||||
def grep_it(self, prog, path):
|
||||
dir, base = os.path.split(path)
|
||||
list = self.findfiles(dir, base, self.recvar.get())
|
||||
list.sort()
|
||||
self.close()
|
||||
pat = self.engine.getpat()
|
||||
print "Searching %s in %s ..." % (`pat`, path)
|
||||
hits = 0
|
||||
for fn in list:
|
||||
try:
|
||||
f = open(fn)
|
||||
except IOError, msg:
|
||||
print msg
|
||||
continue
|
||||
lineno = 0
|
||||
while 1:
|
||||
block = f.readlines(100000)
|
||||
if not block:
|
||||
break
|
||||
for line in block:
|
||||
lineno = lineno + 1
|
||||
if line[-1:] == '\n':
|
||||
line = line[:-1]
|
||||
if prog.search(line):
|
||||
sys.stdout.write("%s: %s: %s\n" % (fn, lineno, line))
|
||||
hits = hits + 1
|
||||
if hits:
|
||||
if hits == 1:
|
||||
s = ""
|
||||
else:
|
||||
s = "s"
|
||||
print "Found", hits, "hit%s." % s
|
||||
print "(Hint: right-click to open locations.)"
|
||||
else:
|
||||
print "No hits."
|
||||
|
||||
def findfiles(self, dir, base, rec):
|
||||
try:
|
||||
names = os.listdir(dir or os.curdir)
|
||||
except os.error, msg:
|
||||
print msg
|
||||
return []
|
||||
list = []
|
||||
subdirs = []
|
||||
for name in names:
|
||||
fn = os.path.join(dir, name)
|
||||
if os.path.isdir(fn):
|
||||
subdirs.append(fn)
|
||||
else:
|
||||
if fnmatch.fnmatch(name, base):
|
||||
list.append(fn)
|
||||
if rec:
|
||||
for subdir in subdirs:
|
||||
list.extend(self.findfiles(subdir, base, rec))
|
||||
return list
|
||||
|
||||
def close(self, event=None):
|
||||
if self.top:
|
||||
self.top.grab_release()
|
||||
self.top.withdraw()
|
|
@ -1,448 +0,0 @@
|
|||
import os
|
||||
import types
|
||||
import sys
|
||||
import codecs
|
||||
import re
|
||||
import tempfile
|
||||
import tkFileDialog
|
||||
import tkMessageBox
|
||||
from IdleConf import idleconf
|
||||
|
||||
#$ event <<open-window-from-file>>
|
||||
#$ win <Control-o>
|
||||
#$ unix <Control-x><Control-f>
|
||||
|
||||
#$ event <<save-window>>
|
||||
#$ win <Control-s>
|
||||
#$ unix <Control-x><Control-s>
|
||||
|
||||
#$ event <<save-window-as-file>>
|
||||
#$ win <Alt-s>
|
||||
#$ unix <Control-x><Control-w>
|
||||
|
||||
#$ event <<save-copy-of-window-as-file>>
|
||||
#$ win <Alt-Shift-s>
|
||||
#$ unix <Control-x><w>
|
||||
|
||||
#$ event <<print-window>>
|
||||
#$ win <Control-p>
|
||||
#$ unix <Control-x><Control-p>
|
||||
|
||||
try:
|
||||
from codecs import BOM_UTF8
|
||||
except ImportError:
|
||||
# only available since Python 2.3
|
||||
BOM_UTF8 = '\xef\xbb\xbf'
|
||||
|
||||
# Try setting the locale, so that we can find out
|
||||
# what encoding to use
|
||||
try:
|
||||
import locale
|
||||
locale.setlocale(locale.LC_CTYPE, "")
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
encoding = "ascii"
|
||||
if sys.platform == 'win32':
|
||||
# On Windows, we could use "mbcs". However, to give the user
|
||||
# a portable encoding name, we need to find the code page
|
||||
try:
|
||||
encoding = locale.getdefaultlocale()[1]
|
||||
codecs.lookup(encoding)
|
||||
except LookupError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
# Different things can fail here: the locale module may not be
|
||||
# loaded, it may not offer nl_langinfo, or CODESET, or the
|
||||
# resulting codeset may be unknown to Python. We ignore all
|
||||
# these problems, falling back to ASCII
|
||||
encoding = locale.nl_langinfo(locale.CODESET)
|
||||
codecs.lookup(encoding)
|
||||
except (NameError, AttributeError, LookupError):
|
||||
# Try getdefaultlocale well: it parses environment variables,
|
||||
# which may give a clue. Unfortunately, getdefaultlocale has
|
||||
# bugs that can cause ValueError.
|
||||
try:
|
||||
encoding = locale.getdefaultlocale()[1]
|
||||
codecs.lookup(encoding)
|
||||
except (ValueError, LookupError):
|
||||
pass
|
||||
|
||||
encoding = encoding.lower()
|
||||
|
||||
coding_re = re.compile("coding[:=]\s*([-\w_.]+)")
|
||||
def coding_spec(str):
|
||||
|
||||
"""Return the encoding declaration according to PEP 263.
|
||||
Raise LookupError if the encoding is declared but unknown."""
|
||||
|
||||
# Only consider the first two lines
|
||||
str = str.split("\n")[:2]
|
||||
str = "\n".join(str)
|
||||
|
||||
match = coding_re.search(str)
|
||||
if not match:
|
||||
return None
|
||||
name = match.group(1)
|
||||
# Check whether the encoding is known
|
||||
import codecs
|
||||
try:
|
||||
codecs.lookup(name)
|
||||
except LookupError:
|
||||
# The standard encoding error does not indicate the encoding
|
||||
raise LookupError, "Unknown encoding "+name
|
||||
return name
|
||||
|
||||
class IOBinding:
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
self.text = editwin.text
|
||||
self.__id_open = self.text.bind("<<open-window-from-file>>", self.open)
|
||||
self.__id_save = self.text.bind("<<save-window>>", self.save)
|
||||
self.__id_saveas = self.text.bind("<<save-window-as-file>>",
|
||||
self.save_as)
|
||||
self.__id_savecopy = self.text.bind("<<save-copy-of-window-as-file>>",
|
||||
self.save_a_copy)
|
||||
self.__id_print = self.text.bind("<<print-window>>", self.print_window)
|
||||
self.fileencoding = None
|
||||
|
||||
def close(self):
|
||||
# Undo command bindings
|
||||
self.text.unbind("<<open-window-from-file>>", self.__id_open)
|
||||
self.text.unbind("<<save-window>>", self.__id_save)
|
||||
self.text.unbind("<<save-window-as-file>>",self.__id_saveas)
|
||||
self.text.unbind("<<save-copy-of-window-as-file>>", self.__id_savecopy)
|
||||
self.text.unbind("<<print-window>>", self.__id_print)
|
||||
# Break cycles
|
||||
self.editwin = None
|
||||
self.text = None
|
||||
self.filename_change_hook = None
|
||||
|
||||
def get_saved(self):
|
||||
return self.editwin.get_saved()
|
||||
|
||||
def set_saved(self, flag):
|
||||
self.editwin.set_saved(flag)
|
||||
|
||||
def reset_undo(self):
|
||||
self.editwin.reset_undo()
|
||||
|
||||
filename_change_hook = None
|
||||
|
||||
def set_filename_change_hook(self, hook):
|
||||
self.filename_change_hook = hook
|
||||
|
||||
filename = None
|
||||
|
||||
def set_filename(self, filename):
|
||||
self.filename = filename
|
||||
self.set_saved(1)
|
||||
if self.filename_change_hook:
|
||||
self.filename_change_hook()
|
||||
|
||||
def open(self, event):
|
||||
if self.editwin.flist:
|
||||
filename = self.askopenfile()
|
||||
if filename:
|
||||
self.editwin.flist.open(filename)
|
||||
else:
|
||||
self.text.focus_set()
|
||||
return "break"
|
||||
# Code for use outside IDLE:
|
||||
if self.get_saved():
|
||||
reply = self.maybesave()
|
||||
if reply == "cancel":
|
||||
self.text.focus_set()
|
||||
return "break"
|
||||
filename = self.askopenfile()
|
||||
if filename:
|
||||
self.loadfile(filename)
|
||||
else:
|
||||
self.text.focus_set()
|
||||
return "break"
|
||||
|
||||
def loadfile(self, filename):
|
||||
try:
|
||||
f = open(filename)
|
||||
chars = f.read()
|
||||
f.close()
|
||||
except IOError, msg:
|
||||
tkMessageBox.showerror("I/O Error", str(msg), master=self.text)
|
||||
return False
|
||||
|
||||
chars = self.decode(chars)
|
||||
|
||||
self.text.delete("1.0", "end")
|
||||
self.set_filename(None)
|
||||
self.text.insert("1.0", chars)
|
||||
self.reset_undo()
|
||||
self.set_filename(filename)
|
||||
self.text.mark_set("insert", "1.0")
|
||||
self.text.see("insert")
|
||||
return True
|
||||
|
||||
def decode(self, chars):
|
||||
# Try to create a Unicode string. If that fails, let Tcl try
|
||||
# its best
|
||||
|
||||
# Check presence of a UTF-8 signature first
|
||||
if chars.startswith(BOM_UTF8):
|
||||
try:
|
||||
chars = chars[3:].decode("utf-8")
|
||||
except UnicodeError:
|
||||
# has UTF-8 signature, but fails to decode...
|
||||
return chars
|
||||
else:
|
||||
# Indicates that this file originally had a BOM
|
||||
self.fileencoding = BOM_UTF8
|
||||
return chars
|
||||
|
||||
# Next look for coding specification
|
||||
try:
|
||||
enc = coding_spec(chars)
|
||||
except LookupError, name:
|
||||
tkMessageBox.showerror(
|
||||
title="Error loading the file",
|
||||
message="The encoding '%s' is not known to this Python "\
|
||||
"installation. The file may not display correctly" % name,
|
||||
master = self.text)
|
||||
enc = None
|
||||
|
||||
if enc:
|
||||
try:
|
||||
return unicode(chars, enc)
|
||||
except UnicodeError:
|
||||
pass
|
||||
|
||||
# If it is ASCII, we need not to record anything
|
||||
try:
|
||||
return unicode(chars, 'ascii')
|
||||
except UnicodeError:
|
||||
pass
|
||||
|
||||
# Finally, try the locale's encoding. This is deprecated;
|
||||
# the user should declare a non-ASCII encoding
|
||||
try:
|
||||
chars = unicode(chars, encoding)
|
||||
self.fileencoding = encoding
|
||||
except UnicodeError:
|
||||
pass
|
||||
return chars
|
||||
|
||||
def maybesave(self):
|
||||
if self.get_saved():
|
||||
return "yes"
|
||||
message = "Do you want to save %s before closing?" % (
|
||||
self.filename or "this untitled document")
|
||||
m = tkMessageBox.Message(
|
||||
title="Save On Close",
|
||||
message=message,
|
||||
icon=tkMessageBox.QUESTION,
|
||||
type=tkMessageBox.YESNOCANCEL,
|
||||
master=self.text)
|
||||
reply = m.show()
|
||||
if reply == "yes":
|
||||
self.save(None)
|
||||
if not self.get_saved():
|
||||
reply = "cancel"
|
||||
self.text.focus_set()
|
||||
return reply
|
||||
|
||||
def save(self, event):
|
||||
if not self.filename:
|
||||
self.save_as(event)
|
||||
else:
|
||||
if self.writefile(self.filename):
|
||||
self.set_saved(1)
|
||||
self.text.focus_set()
|
||||
return "break"
|
||||
|
||||
def save_as(self, event):
|
||||
filename = self.asksavefile()
|
||||
if filename:
|
||||
if self.writefile(filename):
|
||||
self.set_filename(filename)
|
||||
self.set_saved(1)
|
||||
self.text.focus_set()
|
||||
return "break"
|
||||
|
||||
def save_a_copy(self, event):
|
||||
filename = self.asksavefile()
|
||||
if filename:
|
||||
self.writefile(filename)
|
||||
self.text.focus_set()
|
||||
return "break"
|
||||
|
||||
def print_window(self, event):
|
||||
if self.get_saved():
|
||||
filename = self.filename
|
||||
else:
|
||||
(tfd, tfn) = tempfile.mkstemp()
|
||||
os.close(tfd)
|
||||
filename = tfn
|
||||
if not self.writefile(filename):
|
||||
os.unlink(tfn)
|
||||
return "break"
|
||||
edconf = idleconf.getsection('EditorWindow')
|
||||
command = edconf.get('print-command')
|
||||
command = command % filename
|
||||
if os.name == 'posix':
|
||||
command = command + " 2>&1"
|
||||
pipe = os.popen(command, "r")
|
||||
output = pipe.read().strip()
|
||||
status = pipe.close()
|
||||
if status:
|
||||
output = "Printing failed (exit status 0x%x)\n" % status + output
|
||||
if output:
|
||||
output = "Printing command: %s\n" % repr(command) + output
|
||||
tkMessageBox.showerror("Print status", output, master=self.text)
|
||||
return "break"
|
||||
|
||||
def writefile(self, filename):
|
||||
self.fixlastline()
|
||||
chars = self.encode(self.text.get("1.0", "end-1c"))
|
||||
try:
|
||||
f = open(filename, "w")
|
||||
f.write(chars)
|
||||
f.close()
|
||||
## print "saved to", `filename`
|
||||
return True
|
||||
except IOError, msg:
|
||||
tkMessageBox.showerror("I/O Error", str(msg),
|
||||
master=self.text)
|
||||
return False
|
||||
|
||||
def encode(self, chars):
|
||||
if isinstance(chars, types.StringType):
|
||||
# This is either plain ASCII, or Tk was returning mixed-encoding
|
||||
# text to us. Don't try to guess further.
|
||||
return chars
|
||||
|
||||
# See whether there is anything non-ASCII in it.
|
||||
# If not, no need to figure out the encoding.
|
||||
try:
|
||||
return chars.encode('ascii')
|
||||
except UnicodeError:
|
||||
pass
|
||||
|
||||
# If there is an encoding declared, try this first.
|
||||
try:
|
||||
enc = coding_spec(chars)
|
||||
failed = None
|
||||
except LookupError, msg:
|
||||
failed = msg
|
||||
enc = None
|
||||
if enc:
|
||||
try:
|
||||
return chars.encode(enc)
|
||||
except UnicodeError:
|
||||
failed = "Invalid encoding '%s'" % enc
|
||||
|
||||
if failed:
|
||||
tkMessageBox.showerror(
|
||||
"I/O Error",
|
||||
"%s. Saving as UTF-8" % failed,
|
||||
master = self.text)
|
||||
|
||||
# If there was a UTF-8 signature, use that. This should not fail
|
||||
if self.fileencoding == BOM_UTF8 or failed:
|
||||
return BOM_UTF8 + chars.encode("utf-8")
|
||||
|
||||
# Try the original file encoding next, if any
|
||||
if self.fileencoding:
|
||||
try:
|
||||
return chars.encode(self.fileencoding)
|
||||
except UnicodeError:
|
||||
tkMessageBox.showerror(
|
||||
"I/O Error",
|
||||
"Cannot save this as '%s' anymore. Saving as UTF-8" % self.fileencoding,
|
||||
master = self.text)
|
||||
return BOM_UTF8 + chars.encode("utf-8")
|
||||
|
||||
# Nothing was declared, and we had not determined an encoding
|
||||
# on loading. Recommend an encoding line.
|
||||
try:
|
||||
chars = chars.encode(encoding)
|
||||
enc = encoding
|
||||
except UnicodeError:
|
||||
chars = BOM_UTF8 + chars.encode("utf-8")
|
||||
enc = "utf-8"
|
||||
tkMessageBox.showerror(
|
||||
"I/O Error",
|
||||
"Non-ASCII found, yet no encoding declared. Add a line like\n"
|
||||
"# -*- coding: %s -*- \nto your file" % enc,
|
||||
master = self.text)
|
||||
return chars
|
||||
|
||||
def fixlastline(self):
|
||||
c = self.text.get("end-2c")
|
||||
if c != '\n':
|
||||
self.text.insert("end-1c", "\n")
|
||||
|
||||
opendialog = None
|
||||
savedialog = None
|
||||
|
||||
filetypes = [
|
||||
("Python and text files", "*.py *.pyw *.txt", "TEXT"),
|
||||
("All text files", "*", "TEXT"),
|
||||
("All files", "*"),
|
||||
]
|
||||
|
||||
def askopenfile(self):
|
||||
dir, base = self.defaultfilename("open")
|
||||
if not self.opendialog:
|
||||
self.opendialog = tkFileDialog.Open(master=self.text,
|
||||
filetypes=self.filetypes)
|
||||
return self.opendialog.show(initialdir=dir, initialfile=base)
|
||||
|
||||
def defaultfilename(self, mode="open"):
|
||||
if self.filename:
|
||||
return os.path.split(self.filename)
|
||||
else:
|
||||
try:
|
||||
pwd = os.getcwd()
|
||||
except os.error:
|
||||
pwd = ""
|
||||
return pwd, ""
|
||||
|
||||
def asksavefile(self):
|
||||
dir, base = self.defaultfilename("save")
|
||||
if not self.savedialog:
|
||||
self.savedialog = tkFileDialog.SaveAs(master=self.text,
|
||||
filetypes=self.filetypes)
|
||||
return self.savedialog.show(initialdir=dir, initialfile=base)
|
||||
|
||||
|
||||
def test():
|
||||
root = Tk()
|
||||
class MyEditWin:
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
self.flist = None
|
||||
self.text.bind("<Control-o>", self.open)
|
||||
self.text.bind("<Control-s>", self.save)
|
||||
self.text.bind("<Alt-s>", self.save_as)
|
||||
self.text.bind("<Alt-z>", self.save_a_copy)
|
||||
def get_saved(self): return 0
|
||||
def set_saved(self, flag): pass
|
||||
def reset_undo(self): pass
|
||||
def open(self, event):
|
||||
self.text.event_generate("<<open-window-from-file>>")
|
||||
def save(self, event):
|
||||
self.text.event_generate("<<save-window>>")
|
||||
def save_as(self, event):
|
||||
self.text.event_generate("<<save-window-as-file>>")
|
||||
def save_a_copy(self, event):
|
||||
self.text.event_generate("<<save-copy-of-window-as-file>>")
|
||||
text = Text(root)
|
||||
text.pack()
|
||||
text.focus_set()
|
||||
editwin = MyEditWin(text)
|
||||
io = IOBinding(editwin)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
from Tkinter import *
|
||||
test()
|
Binary file not shown.
Before Width: | Height: | Size: 120 B |
Binary file not shown.
Before Width: | Height: | Size: 56 B |
Binary file not shown.
Before Width: | Height: | Size: 125 B |
Binary file not shown.
Before Width: | Height: | Size: 79 B |
Binary file not shown.
Before Width: | Height: | Size: 125 B |
Binary file not shown.
Before Width: | Height: | Size: 85 B |
|
@ -1,113 +0,0 @@
|
|||
"""Provides access to configuration information"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from ConfigParser import ConfigParser, NoOptionError, NoSectionError
|
||||
|
||||
class IdleConfParser(ConfigParser):
|
||||
|
||||
# these conf sections do not define extensions!
|
||||
builtin_sections = {}
|
||||
for section in ('EditorWindow', 'Colors'):
|
||||
builtin_sections[section] = section
|
||||
|
||||
def getcolor(self, sec, name):
|
||||
"""Return a dictionary with foreground and background colors
|
||||
|
||||
The return value is appropriate for passing to Tkinter in, e.g.,
|
||||
a tag_config call.
|
||||
"""
|
||||
fore = self.getdef(sec, name + "-foreground")
|
||||
back = self.getdef(sec, name + "-background")
|
||||
return {"foreground": fore,
|
||||
"background": back}
|
||||
|
||||
def getdef(self, sec, options, raw=0, vars=None, default=None):
|
||||
"""Get an option value for given section or return default"""
|
||||
try:
|
||||
return self.get(sec, options, raw, vars)
|
||||
except (NoSectionError, NoOptionError):
|
||||
return default
|
||||
|
||||
def getsection(self, section):
|
||||
"""Return a SectionConfigParser object"""
|
||||
return SectionConfigParser(section, self)
|
||||
|
||||
def getextensions(self):
|
||||
exts = []
|
||||
for sec in self.sections():
|
||||
if self.builtin_sections.has_key(sec):
|
||||
continue
|
||||
# enable is a bool, but it may not be defined
|
||||
if self.getdef(sec, 'enable') != '0':
|
||||
exts.append(sec)
|
||||
return exts
|
||||
|
||||
def reload(self):
|
||||
global idleconf
|
||||
idleconf = IdleConfParser()
|
||||
load(_dir) # _dir is a global holding the last directory loaded
|
||||
|
||||
class SectionConfigParser:
|
||||
"""A ConfigParser object specialized for one section
|
||||
|
||||
This class has all the get methods that a regular ConfigParser does,
|
||||
but without requiring a section argument.
|
||||
"""
|
||||
def __init__(self, section, config):
|
||||
self.section = section
|
||||
self.config = config
|
||||
|
||||
def options(self):
|
||||
return self.config.options(self.section)
|
||||
|
||||
def get(self, options, raw=0, vars=None):
|
||||
return self.config.get(self.section, options, raw, vars)
|
||||
|
||||
def getdef(self, options, raw=0, vars=None, default=None):
|
||||
return self.config.getdef(self.section, options, raw, vars, default)
|
||||
|
||||
def getint(self, option):
|
||||
return self.config.getint(self.section, option)
|
||||
|
||||
def getfloat(self, option):
|
||||
return self.config.getint(self.section, option)
|
||||
|
||||
def getboolean(self, option):
|
||||
return self.config.getint(self.section, option)
|
||||
|
||||
def getcolor(self, option):
|
||||
return self.config.getcolor(self.section, option)
|
||||
|
||||
def load(dir):
|
||||
"""Load IDLE configuration files based on IDLE install in dir
|
||||
|
||||
Attempts to load two config files:
|
||||
dir/config.txt
|
||||
dir/config-[win/mac/unix].txt
|
||||
dir/config-%(sys.platform)s.txt
|
||||
~/.idle
|
||||
"""
|
||||
global _dir
|
||||
_dir = dir
|
||||
|
||||
if sys.platform[:3] == 'win':
|
||||
genplatfile = os.path.join(dir, "config-win.txt")
|
||||
# XXX don't know what the platform string is on a Mac
|
||||
elif sys.platform[:3] == 'mac':
|
||||
genplatfile = os.path.join(dir, "config-mac.txt")
|
||||
else:
|
||||
genplatfile = os.path.join(dir, "config-unix.txt")
|
||||
|
||||
platfile = os.path.join(dir, "config-%s.txt" % sys.platform)
|
||||
|
||||
try:
|
||||
homedir = os.environ['HOME']
|
||||
except KeyError:
|
||||
homedir = os.getcwd()
|
||||
|
||||
idleconf.read((os.path.join(dir, "config.txt"), genplatfile, platfile,
|
||||
os.path.join(homedir, ".idle")))
|
||||
|
||||
idleconf = IdleConfParser()
|
||||
load(os.path.dirname(__file__))
|
|
@ -1,86 +0,0 @@
|
|||
class History:
|
||||
|
||||
def __init__(self, text, output_sep = "\n"):
|
||||
self.text = text
|
||||
self.history = []
|
||||
self.history_prefix = None
|
||||
self.history_pointer = None
|
||||
self.output_sep = output_sep
|
||||
text.bind("<<history-previous>>", self.history_prev)
|
||||
text.bind("<<history-next>>", self.history_next)
|
||||
|
||||
def history_next(self, event):
|
||||
self.history_do(0)
|
||||
return "break"
|
||||
|
||||
def history_prev(self, event):
|
||||
self.history_do(1)
|
||||
return "break"
|
||||
|
||||
def _get_source(self, start, end):
|
||||
# Get source code from start index to end index. Lines in the
|
||||
# text control may be separated by sys.ps2 .
|
||||
lines = self.text.get(start, end).split(self.output_sep)
|
||||
return "\n".join(lines)
|
||||
|
||||
def _put_source(self, where, source):
|
||||
output = self.output_sep.join(source.split("\n"))
|
||||
self.text.insert(where, output)
|
||||
|
||||
def history_do(self, reverse):
|
||||
nhist = len(self.history)
|
||||
pointer = self.history_pointer
|
||||
prefix = self.history_prefix
|
||||
if pointer is not None and prefix is not None:
|
||||
if self.text.compare("insert", "!=", "end-1c") or \
|
||||
self._get_source("iomark", "end-1c") != self.history[pointer]:
|
||||
pointer = prefix = None
|
||||
if pointer is None or prefix is None:
|
||||
prefix = self._get_source("iomark", "end-1c")
|
||||
if reverse:
|
||||
pointer = nhist
|
||||
else:
|
||||
pointer = -1
|
||||
nprefix = len(prefix)
|
||||
while 1:
|
||||
if reverse:
|
||||
pointer = pointer - 1
|
||||
else:
|
||||
pointer = pointer + 1
|
||||
if pointer < 0 or pointer >= nhist:
|
||||
self.text.bell()
|
||||
if self._get_source("iomark", "end-1c") != prefix:
|
||||
self.text.delete("iomark", "end-1c")
|
||||
self._put_source("iomark", prefix)
|
||||
pointer = prefix = None
|
||||
break
|
||||
item = self.history[pointer]
|
||||
if item[:nprefix] == prefix and len(item) > nprefix:
|
||||
self.text.delete("iomark", "end-1c")
|
||||
self._put_source("iomark", item)
|
||||
break
|
||||
self.text.mark_set("insert", "end-1c")
|
||||
self.text.see("insert")
|
||||
self.text.tag_remove("sel", "1.0", "end")
|
||||
self.history_pointer = pointer
|
||||
self.history_prefix = prefix
|
||||
|
||||
def history_store(self, source):
|
||||
source = source.strip()
|
||||
if len(source) > 2:
|
||||
# avoid duplicates
|
||||
try:
|
||||
self.history.remove(source)
|
||||
except ValueError:
|
||||
pass
|
||||
self.history.append(source)
|
||||
self.history_pointer = None
|
||||
self.history_prefix = None
|
||||
|
||||
def recall(self, s):
|
||||
s = s.strip()
|
||||
self.text.tag_remove("sel", "1.0", "end")
|
||||
self.text.delete("iomark", "end-1c")
|
||||
self.text.mark_set("insert", "end-1c")
|
||||
self.text.insert("insert", s)
|
||||
self.text.see("insert")
|
|
@ -1,4 +0,0 @@
|
|||
include *.txt
|
||||
include idle
|
||||
include MANIFEST.in
|
||||
include MANIFEST
|
|
@ -1,137 +0,0 @@
|
|||
# One or more ScrolledLists with HSeparators between them.
|
||||
# There is a hierarchical relationship between them:
|
||||
# the right list displays the substructure of the selected item
|
||||
# in the left list.
|
||||
|
||||
from Tkinter import *
|
||||
from WindowList import ListedToplevel
|
||||
from Separator import HSeparator
|
||||
from ScrolledList import ScrolledList
|
||||
|
||||
class MultiScrolledLists:
|
||||
|
||||
def __init__(self, root, nlists=2):
|
||||
assert nlists >= 1
|
||||
self.root = root
|
||||
self.nlists = nlists
|
||||
self.path = []
|
||||
# create top
|
||||
self.top = top = ListedToplevel(root)
|
||||
top.protocol("WM_DELETE_WINDOW", self.close)
|
||||
top.bind("<Escape>", self.close)
|
||||
self.settitle()
|
||||
# create frames and separators in between
|
||||
self.frames = []
|
||||
self.separators = []
|
||||
last = top
|
||||
for i in range(nlists-1):
|
||||
sepa = HSeparator(last)
|
||||
self.separators.append(sepa)
|
||||
frame, last = sepa.parts()
|
||||
self.frames.append(frame)
|
||||
self.frames.append(last)
|
||||
# create labels and lists
|
||||
self.labels = []
|
||||
self.lists = []
|
||||
for i in range(nlists):
|
||||
frame = self.frames[i]
|
||||
label = Label(frame, text=self.subtitle(i),
|
||||
relief="groove", borderwidth=2)
|
||||
label.pack(fill="x")
|
||||
self.labels.append(label)
|
||||
list = ScrolledList(frame, width=self.width(i),
|
||||
height=self.height(i))
|
||||
self.lists.append(list)
|
||||
list.on_select = \
|
||||
lambda index, i=i, self=self: self.on_select(index, i)
|
||||
list.on_double = \
|
||||
lambda index, i=i, self=self: self.on_double(index, i)
|
||||
# fill leftmost list (rest get filled on demand)
|
||||
self.fill(0)
|
||||
# XXX one after_idle isn't enough; two are...
|
||||
top.after_idle(self.call_pack_propagate_1)
|
||||
|
||||
def call_pack_propagate_1(self):
|
||||
self.top.after_idle(self.call_pack_propagate)
|
||||
|
||||
def call_pack_propagate(self):
|
||||
for frame in self.frames:
|
||||
frame.pack_propagate(0)
|
||||
|
||||
def close(self, event=None):
|
||||
self.top.destroy()
|
||||
|
||||
def settitle(self):
|
||||
short = self.shorttitle()
|
||||
long = self.longtitle()
|
||||
if short and long:
|
||||
title = short + " - " + long
|
||||
elif short:
|
||||
title = short
|
||||
elif long:
|
||||
title = long
|
||||
else:
|
||||
title = "Untitled"
|
||||
icon = short or long or title
|
||||
self.top.wm_title(title)
|
||||
self.top.wm_iconname(icon)
|
||||
|
||||
def longtitle(self):
|
||||
# override this
|
||||
return "Multi Scrolled Lists"
|
||||
|
||||
def shorttitle(self):
|
||||
# override this
|
||||
return None
|
||||
|
||||
def width(self, i):
|
||||
# override this
|
||||
return 20
|
||||
|
||||
def height(self, i):
|
||||
# override this
|
||||
return 10
|
||||
|
||||
def subtitle(self, i):
|
||||
# override this
|
||||
return "Column %d" % i
|
||||
|
||||
def fill(self, i):
|
||||
for k in range(i, self.nlists):
|
||||
self.lists[k].clear()
|
||||
self.labels[k].configure(text=self.subtitle(k))
|
||||
list = self.lists[i]
|
||||
l = self.items(i)
|
||||
for s in l:
|
||||
list.append(s)
|
||||
|
||||
def on_select(self, index, i):
|
||||
item = self.lists[i].get(index)
|
||||
del self.path[i:]
|
||||
self.path.append(item)
|
||||
if i+1 < self.nlists:
|
||||
self.fill(i+1)
|
||||
|
||||
def items(self, i):
|
||||
# override this
|
||||
l = []
|
||||
for k in range(10):
|
||||
s = str(k)
|
||||
if i > 0:
|
||||
s = self.path[i-1] + "." + s
|
||||
l.append(s)
|
||||
return l
|
||||
|
||||
def on_double(self, index, i):
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
root = Tk()
|
||||
quit = Button(root, text="Exit", command=root.destroy)
|
||||
quit.pack()
|
||||
MultiScrolledLists(root, 4)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,32 +0,0 @@
|
|||
from Tkinter import *
|
||||
|
||||
class MultiStatusBar(Frame):
|
||||
|
||||
def __init__(self, master=None, **kw):
|
||||
if master is None:
|
||||
master = Tk()
|
||||
apply(Frame.__init__, (self, master), kw)
|
||||
self.labels = {}
|
||||
|
||||
def set_label(self, name, text='', side=LEFT):
|
||||
if not self.labels.has_key(name):
|
||||
label = Label(self, bd=1, relief=SUNKEN, anchor=W)
|
||||
label.pack(side=side)
|
||||
self.labels[name] = label
|
||||
else:
|
||||
label = self.labels[name]
|
||||
label.config(text=text)
|
||||
|
||||
def _test():
|
||||
b = Frame()
|
||||
c = Text(b)
|
||||
c.pack(side=TOP)
|
||||
a = MultiStatusBar(b)
|
||||
a.set_label("one", "hello")
|
||||
a.set_label("two", "world")
|
||||
a.pack(side=BOTTOM, fill=X)
|
||||
b.pack()
|
||||
b.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
_test()
|
|
@ -1,130 +0,0 @@
|
|||
(For a more detailed change log, see the file ChangeLog.)
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
New in IDLE 0.5 (2/15/2000)
|
||||
-------------------------
|
||||
|
||||
Tons of stuff, much of it contributed by Tim Peters and Mark Hammond:
|
||||
|
||||
- Status bar, displaying current line/column (Moshe Zadka).
|
||||
|
||||
- Better stack viewer, using tree widget. (XXX Only used by Stack
|
||||
Viewer menu, not by the debugger.)
|
||||
|
||||
- Format paragraph now recognizes Python block comments and reformats
|
||||
them correctly (MH)
|
||||
|
||||
- New version of pyclbr.py parses top-level functions and understands
|
||||
much more of Python's syntax; this is reflected in the class and path
|
||||
browsers (TP)
|
||||
|
||||
- Much better auto-indent; knows how to indent the insides of
|
||||
multi-line statements (TP)
|
||||
|
||||
- Call tip window pops up when you type the name of a known function
|
||||
followed by an open parenthesis. Hit ESC or click elsewhere in the
|
||||
window to close the tip window (MH)
|
||||
|
||||
- Comment out region now inserts ## to make it stand out more (TP)
|
||||
|
||||
- New path and class browsers based on a tree widget that looks
|
||||
familiar to Windows users
|
||||
|
||||
- Reworked script running commands to be more intuitive: I/O now
|
||||
always goes to the *Python Shell* window, and raw_input() works
|
||||
correctly. You use F5 to import/reload a module: this adds the module
|
||||
name to the __main__ namespace. You use Control-F5 to run a script:
|
||||
this runs the script *in* the __main__ namespace. The latter also
|
||||
sets sys.argv[] to the script name
|
||||
|
||||
New in IDLE 0.4 (4/7/99)
|
||||
------------------------
|
||||
|
||||
Most important change: a new menu entry "File -> Path browser", shows
|
||||
a 4-column hierarchical browser which lets you browse sys.path,
|
||||
directories, modules, and classes. Yes, it's a superset of the Class
|
||||
browser menu entry. There's also a new internal module,
|
||||
MultiScrolledLists.py, which provides the framework for this dialog.
|
||||
|
||||
New in IDLE 0.3 (2/17/99)
|
||||
-------------------------
|
||||
|
||||
Most important changes:
|
||||
|
||||
- Enabled support for running a module, with or without the debugger.
|
||||
Output goes to a new window. Pressing F5 in a module is effectively a
|
||||
reload of that module; Control-F5 loads it under the debugger.
|
||||
|
||||
- Re-enable tearing off the Windows menu, and make a torn-off Windows
|
||||
menu update itself whenever a window is opened or closed.
|
||||
|
||||
- Menu items can now be have a checkbox (when the menu label starts
|
||||
with "!"); use this for the Debugger and "Auto-open stack viewer"
|
||||
(was: JIT stack viewer) menu items.
|
||||
|
||||
- Added a Quit button to the Debugger API.
|
||||
|
||||
- The current directory is explicitly inserted into sys.path.
|
||||
|
||||
- Fix the debugger (when using Python 1.5.2b2) to use canonical
|
||||
filenames for breakpoints, so these actually work. (There's still a
|
||||
lot of work to be done to the management of breakpoints in the
|
||||
debugger though.)
|
||||
|
||||
- Closing a window that is still colorizing now actually works.
|
||||
|
||||
- Allow dragging of the separator between the two list boxes in the
|
||||
class browser.
|
||||
|
||||
- Bind ESC to "close window" of the debugger, stack viewer and class
|
||||
browser. It removes the selection highlighting in regular text
|
||||
windows. (These are standard Windows conventions.)
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
New in IDLE 0.2 (1/8/99)
|
||||
------------------------
|
||||
|
||||
Lots of changes; here are the highlights:
|
||||
|
||||
General:
|
||||
|
||||
- You can now write and configure your own IDLE extension modules; see
|
||||
extend.txt.
|
||||
|
||||
|
||||
File menu:
|
||||
|
||||
The command to open the Python shell window is now in the File menu.
|
||||
|
||||
|
||||
Edit menu:
|
||||
|
||||
New Find dialog with more options; replace dialog; find in files dialog.
|
||||
|
||||
Commands to tabify or untabify a region.
|
||||
|
||||
Command to format a paragraph.
|
||||
|
||||
|
||||
Debug menu:
|
||||
|
||||
JIT (Just-In-Time) stack viewer toggle -- if set, the stack viewer
|
||||
automaticall pops up when you get a traceback.
|
||||
|
||||
Windows menu:
|
||||
|
||||
Zoom height -- make the window full height.
|
||||
|
||||
|
||||
Help menu:
|
||||
|
||||
The help text now show up in a regular window so you can search and
|
||||
even edit it if you like.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
IDLE 0.1 was distributed with the Python 1.5.2b1 release on 12/22/98.
|
||||
|
||||
======================================================================
|
|
@ -1,151 +0,0 @@
|
|||
# XXX TO DO:
|
||||
# - popup menu
|
||||
# - support partial or total redisplay
|
||||
# - more doc strings
|
||||
# - tooltips
|
||||
|
||||
# object browser
|
||||
|
||||
# XXX TO DO:
|
||||
# - for classes/modules, add "open source" to object browser
|
||||
|
||||
from TreeWidget import TreeItem, TreeNode, ScrolledCanvas
|
||||
|
||||
from repr import Repr
|
||||
|
||||
myrepr = Repr()
|
||||
myrepr.maxstring = 100
|
||||
myrepr.maxother = 100
|
||||
|
||||
class ObjectTreeItem(TreeItem):
|
||||
def __init__(self, labeltext, object, setfunction=None):
|
||||
self.labeltext = labeltext
|
||||
self.object = object
|
||||
self.setfunction = setfunction
|
||||
def GetLabelText(self):
|
||||
return self.labeltext
|
||||
def GetText(self):
|
||||
return myrepr.repr(self.object)
|
||||
def GetIconName(self):
|
||||
if not self.IsExpandable():
|
||||
return "python"
|
||||
def IsEditable(self):
|
||||
return self.setfunction is not None
|
||||
def SetText(self, text):
|
||||
try:
|
||||
value = eval(text)
|
||||
self.setfunction(value)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
self.object = value
|
||||
def IsExpandable(self):
|
||||
return not not dir(self.object)
|
||||
def GetSubList(self):
|
||||
keys = dir(self.object)
|
||||
sublist = []
|
||||
for key in keys:
|
||||
try:
|
||||
value = getattr(self.object, key)
|
||||
except AttributeError:
|
||||
continue
|
||||
item = make_objecttreeitem(
|
||||
str(key) + " =",
|
||||
value,
|
||||
lambda value, key=key, object=self.object:
|
||||
setattr(object, key, value))
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
class InstanceTreeItem(ObjectTreeItem):
|
||||
def IsExpandable(self):
|
||||
return True
|
||||
def GetSubList(self):
|
||||
sublist = ObjectTreeItem.GetSubList(self)
|
||||
sublist.insert(0,
|
||||
make_objecttreeitem("__class__ =", self.object.__class__))
|
||||
return sublist
|
||||
|
||||
class ClassTreeItem(ObjectTreeItem):
|
||||
def IsExpandable(self):
|
||||
return True
|
||||
def GetSubList(self):
|
||||
sublist = ObjectTreeItem.GetSubList(self)
|
||||
if len(self.object.__bases__) == 1:
|
||||
item = make_objecttreeitem("__bases__[0] =",
|
||||
self.object.__bases__[0])
|
||||
else:
|
||||
item = make_objecttreeitem("__bases__ =", self.object.__bases__)
|
||||
sublist.insert(0, item)
|
||||
return sublist
|
||||
|
||||
class AtomicObjectTreeItem(ObjectTreeItem):
|
||||
def IsExpandable(self):
|
||||
return 0
|
||||
|
||||
class SequenceTreeItem(ObjectTreeItem):
|
||||
def IsExpandable(self):
|
||||
return len(self.object) > 0
|
||||
def keys(self):
|
||||
return range(len(self.object))
|
||||
def GetSubList(self):
|
||||
sublist = []
|
||||
for key in self.keys():
|
||||
try:
|
||||
value = self.object[key]
|
||||
except KeyError:
|
||||
continue
|
||||
def setfunction(value, key=key, object=self.object):
|
||||
object[key] = value
|
||||
item = make_objecttreeitem(`key` + ":", value, setfunction)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
class DictTreeItem(SequenceTreeItem):
|
||||
def keys(self):
|
||||
keys = self.object.keys()
|
||||
try:
|
||||
keys.sort()
|
||||
except:
|
||||
pass
|
||||
return keys
|
||||
|
||||
from types import *
|
||||
|
||||
dispatch = {
|
||||
IntType: AtomicObjectTreeItem,
|
||||
LongType: AtomicObjectTreeItem,
|
||||
FloatType: AtomicObjectTreeItem,
|
||||
StringType: AtomicObjectTreeItem,
|
||||
TupleType: SequenceTreeItem,
|
||||
ListType: SequenceTreeItem,
|
||||
DictType: DictTreeItem,
|
||||
InstanceType: InstanceTreeItem,
|
||||
ClassType: ClassTreeItem,
|
||||
}
|
||||
|
||||
def make_objecttreeitem(labeltext, object, setfunction=None):
|
||||
t = type(object)
|
||||
if dispatch.has_key(t):
|
||||
c = dispatch[t]
|
||||
else:
|
||||
c = ObjectTreeItem
|
||||
return c(labeltext, object, setfunction)
|
||||
|
||||
# Test script
|
||||
|
||||
def _test():
|
||||
import sys
|
||||
from Tkinter import Tk
|
||||
root = Tk()
|
||||
root.configure(bd=0, bg="yellow")
|
||||
root.focus_set()
|
||||
sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1)
|
||||
sc.frame.pack(expand=1, fill="both")
|
||||
item = make_objecttreeitem("sys", sys)
|
||||
node = TreeNode(sc.canvas, None, item)
|
||||
node.update()
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
_test()
|
|
@ -1,275 +0,0 @@
|
|||
import sys
|
||||
import os
|
||||
from Tkinter import *
|
||||
import linecache
|
||||
from repr import Repr
|
||||
from WindowList import ListedToplevel
|
||||
|
||||
from ScrolledList import ScrolledList
|
||||
|
||||
|
||||
class StackBrowser:
|
||||
|
||||
def __init__(self, root, flist, stack=None):
|
||||
self.top = top = ListedToplevel(root)
|
||||
top.protocol("WM_DELETE_WINDOW", self.close)
|
||||
top.bind("<Key-Escape>", self.close)
|
||||
top.wm_title("Stack viewer")
|
||||
top.wm_iconname("Stack")
|
||||
# Create help label
|
||||
self.helplabel = Label(top,
|
||||
text="Click once to view variables; twice for source",
|
||||
borderwidth=2, relief="groove")
|
||||
self.helplabel.pack(fill="x")
|
||||
#
|
||||
self.sv = StackViewer(top, flist, self)
|
||||
if stack is None:
|
||||
stack = get_stack()
|
||||
self.sv.load_stack(stack)
|
||||
|
||||
def close(self, event=None):
|
||||
self.top.destroy()
|
||||
|
||||
localsframe = None
|
||||
localsviewer = None
|
||||
localsdict = None
|
||||
globalsframe = None
|
||||
globalsviewer = None
|
||||
globalsdict = None
|
||||
curframe = None
|
||||
|
||||
def show_frame(self, (frame, lineno)):
|
||||
if frame is self.curframe:
|
||||
return
|
||||
self.curframe = None
|
||||
if frame.f_globals is not self.globalsdict:
|
||||
self.show_globals(frame)
|
||||
self.show_locals(frame)
|
||||
self.curframe = frame
|
||||
|
||||
def show_globals(self, frame):
|
||||
title = "Global Variables"
|
||||
if frame.f_globals.has_key("__name__"):
|
||||
try:
|
||||
name = str(frame.f_globals["__name__"]) + ""
|
||||
except:
|
||||
name = ""
|
||||
if name:
|
||||
title = title + " in module " + name
|
||||
self.globalsdict = None
|
||||
if self.globalsviewer:
|
||||
self.globalsviewer.close()
|
||||
self.globalsviewer = None
|
||||
if not self.globalsframe:
|
||||
self.globalsframe = Frame(self.top)
|
||||
self.globalsdict = frame.f_globals
|
||||
self.globalsviewer = NamespaceViewer(
|
||||
self.globalsframe,
|
||||
title,
|
||||
self.globalsdict)
|
||||
self.globalsframe.pack(fill="both", side="bottom")
|
||||
|
||||
def show_locals(self, frame):
|
||||
self.localsdict = None
|
||||
if self.localsviewer:
|
||||
self.localsviewer.close()
|
||||
self.localsviewer = None
|
||||
if frame.f_locals is not frame.f_globals:
|
||||
title = "Local Variables"
|
||||
code = frame.f_code
|
||||
funcname = code.co_name
|
||||
if funcname not in ("?", "", None):
|
||||
title = title + " in " + funcname
|
||||
if not self.localsframe:
|
||||
self.localsframe = Frame(self.top)
|
||||
self.localsdict = frame.f_locals
|
||||
self.localsviewer = NamespaceViewer(
|
||||
self.localsframe,
|
||||
title,
|
||||
self.localsdict)
|
||||
self.localsframe.pack(fill="both", side="top")
|
||||
else:
|
||||
if self.localsframe:
|
||||
self.localsframe.forget()
|
||||
|
||||
|
||||
class StackViewer(ScrolledList):
|
||||
|
||||
def __init__(self, master, flist, browser):
|
||||
ScrolledList.__init__(self, master, width=80)
|
||||
self.flist = flist
|
||||
self.browser = browser
|
||||
self.stack = []
|
||||
|
||||
def load_stack(self, stack, index=None):
|
||||
self.stack = stack
|
||||
self.clear()
|
||||
## if len(stack) > 10:
|
||||
## l["height"] = 10
|
||||
## self.topframe.pack(expand=1)
|
||||
## else:
|
||||
## l["height"] = len(stack)
|
||||
## self.topframe.pack(expand=0)
|
||||
for i in range(len(stack)):
|
||||
frame, lineno = stack[i]
|
||||
try:
|
||||
modname = frame.f_globals["__name__"]
|
||||
except:
|
||||
modname = "?"
|
||||
code = frame.f_code
|
||||
filename = code.co_filename
|
||||
funcname = code.co_name
|
||||
sourceline = linecache.getline(filename, lineno)
|
||||
sourceline = sourceline.strip()
|
||||
if funcname in ("?", "", None):
|
||||
item = "%s, line %d: %s" % (modname, lineno, sourceline)
|
||||
else:
|
||||
item = "%s.%s(), line %d: %s" % (modname, funcname,
|
||||
lineno, sourceline)
|
||||
if i == index:
|
||||
item = "> " + item
|
||||
self.append(item)
|
||||
if index is not None:
|
||||
self.select(index)
|
||||
|
||||
def popup_event(self, event):
|
||||
if self.stack:
|
||||
return ScrolledList.popup_event(self, event)
|
||||
|
||||
def fill_menu(self):
|
||||
menu = self.menu
|
||||
menu.add_command(label="Go to source line",
|
||||
command=self.goto_source_line)
|
||||
menu.add_command(label="Show stack frame",
|
||||
command=self.show_stack_frame)
|
||||
|
||||
def on_select(self, index):
|
||||
if 0 <= index < len(self.stack):
|
||||
self.browser.show_frame(self.stack[index])
|
||||
|
||||
def on_double(self, index):
|
||||
self.show_source(index)
|
||||
|
||||
def goto_source_line(self):
|
||||
index = self.listbox.index("active")
|
||||
self.show_source(index)
|
||||
|
||||
def show_stack_frame(self):
|
||||
index = self.listbox.index("active")
|
||||
if 0 <= index < len(self.stack):
|
||||
self.browser.show_frame(self.stack[index])
|
||||
|
||||
def show_source(self, index):
|
||||
if not (0 <= index < len(self.stack)):
|
||||
return
|
||||
frame, lineno = self.stack[index]
|
||||
code = frame.f_code
|
||||
filename = code.co_filename
|
||||
if os.path.isfile(filename):
|
||||
edit = self.flist.open(filename)
|
||||
if edit:
|
||||
edit.gotoline(lineno)
|
||||
|
||||
|
||||
def get_stack(t=None, f=None):
|
||||
if t is None:
|
||||
t = sys.last_traceback
|
||||
stack = []
|
||||
if t and t.tb_frame is f:
|
||||
t = t.tb_next
|
||||
while f is not None:
|
||||
stack.append((f, f.f_lineno))
|
||||
if f is self.botframe:
|
||||
break
|
||||
f = f.f_back
|
||||
stack.reverse()
|
||||
while t is not None:
|
||||
stack.append((t.tb_frame, t.tb_lineno))
|
||||
t = t.tb_next
|
||||
return stack
|
||||
|
||||
|
||||
def getexception(type=None, value=None):
|
||||
if type is None:
|
||||
type = sys.last_type
|
||||
value = sys.last_value
|
||||
if hasattr(type, "__name__"):
|
||||
type = type.__name__
|
||||
s = str(type)
|
||||
if value is not None:
|
||||
s = s + ": " + str(value)
|
||||
return s
|
||||
|
||||
|
||||
class NamespaceViewer:
|
||||
|
||||
def __init__(self, master, title, dict=None):
|
||||
width = 0
|
||||
height = 40
|
||||
if dict:
|
||||
height = 20*len(dict) # XXX 20 == observed height of Entry widget
|
||||
self.master = master
|
||||
self.title = title
|
||||
self.repr = Repr()
|
||||
self.repr.maxstring = 60
|
||||
self.repr.maxother = 60
|
||||
self.frame = frame = Frame(master)
|
||||
self.frame.pack(expand=1, fill="both")
|
||||
self.label = Label(frame, text=title, borderwidth=2, relief="groove")
|
||||
self.label.pack(fill="x")
|
||||
self.vbar = vbar = Scrollbar(frame, name="vbar")
|
||||
vbar.pack(side="right", fill="y")
|
||||
self.canvas = canvas = Canvas(frame,
|
||||
height=min(300, max(40, height)),
|
||||
scrollregion=(0, 0, width, height))
|
||||
canvas.pack(side="left", fill="both", expand=1)
|
||||
vbar["command"] = canvas.yview
|
||||
canvas["yscrollcommand"] = vbar.set
|
||||
self.subframe = subframe = Frame(canvas)
|
||||
self.sfid = canvas.create_window(0, 0, window=subframe, anchor="nw")
|
||||
self.load_dict(dict)
|
||||
|
||||
dict = -1
|
||||
|
||||
def load_dict(self, dict, force=0):
|
||||
if dict is self.dict and not force:
|
||||
return
|
||||
subframe = self.subframe
|
||||
frame = self.frame
|
||||
for c in subframe.children.values():
|
||||
c.destroy()
|
||||
self.dict = None
|
||||
if not dict:
|
||||
l = Label(subframe, text="None")
|
||||
l.grid(row=0, column=0)
|
||||
else:
|
||||
names = dict.keys()
|
||||
names.sort()
|
||||
row = 0
|
||||
for name in names:
|
||||
value = dict[name]
|
||||
svalue = self.repr.repr(value) # repr(value)
|
||||
l = Label(subframe, text=name)
|
||||
l.grid(row=row, column=0, sticky="nw")
|
||||
## l = Label(subframe, text=svalue, justify="l", wraplength=300)
|
||||
l = Entry(subframe, width=0, borderwidth=0)
|
||||
l.insert(0, svalue)
|
||||
## l["state"] = "disabled"
|
||||
l.grid(row=row, column=1, sticky="nw")
|
||||
row = row+1
|
||||
self.dict = dict
|
||||
# XXX Could we use a <Configure> callback for the following?
|
||||
subframe.update_idletasks() # Alas!
|
||||
width = subframe.winfo_reqwidth()
|
||||
height = subframe.winfo_reqheight()
|
||||
canvas = self.canvas
|
||||
self.canvas["scrollregion"] = (0, 0, width, height)
|
||||
if height > 300:
|
||||
canvas["height"] = 300
|
||||
frame.pack(expand=1)
|
||||
else:
|
||||
canvas["height"] = height
|
||||
frame.pack(expand=0)
|
||||
|
||||
def close(self):
|
||||
self.frame.destroy()
|
|
@ -1,157 +0,0 @@
|
|||
from Tkinter import *
|
||||
from EditorWindow import EditorWindow
|
||||
import re
|
||||
import tkMessageBox
|
||||
import IOBinding
|
||||
|
||||
class OutputWindow(EditorWindow):
|
||||
|
||||
"""An editor window that can serve as an output file.
|
||||
|
||||
Also the future base class for the Python shell window.
|
||||
This class has no input facilities.
|
||||
"""
|
||||
|
||||
def __init__(self, *args):
|
||||
apply(EditorWindow.__init__, (self,) + args)
|
||||
self.text.bind("<<goto-file-line>>", self.goto_file_line)
|
||||
|
||||
# Customize EditorWindow
|
||||
|
||||
def ispythonsource(self, filename):
|
||||
# No colorization needed
|
||||
return 0
|
||||
|
||||
def short_title(self):
|
||||
return "Output"
|
||||
|
||||
def maybesave(self):
|
||||
# Override base class method -- don't ask any questions
|
||||
if self.get_saved():
|
||||
return "yes"
|
||||
else:
|
||||
return "no"
|
||||
|
||||
# Act as output file
|
||||
|
||||
def write(self, s, tags=(), mark="insert"):
|
||||
# Tk assumes that byte strings are Latin-1;
|
||||
# we assume that they are in the locale's encoding
|
||||
if isinstance(s, str):
|
||||
try:
|
||||
s = unicode(s, IOBinding.encoding)
|
||||
except UnicodeError:
|
||||
# some other encoding; let Tcl deal with it
|
||||
pass
|
||||
self.text.insert(mark, s, tags)
|
||||
self.text.see(mark)
|
||||
self.text.update()
|
||||
|
||||
def writelines(self, l):
|
||||
map(self.write, l)
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
# Our own right-button menu
|
||||
|
||||
rmenu_specs = [
|
||||
("Go to file/line", "<<goto-file-line>>"),
|
||||
]
|
||||
|
||||
file_line_pats = [
|
||||
r'file "([^"]*)", line (\d+)',
|
||||
r'([^\s]+)\((\d+)\)',
|
||||
r'([^\s]+):\s*(\d+):',
|
||||
]
|
||||
|
||||
file_line_progs = None
|
||||
|
||||
def goto_file_line(self, event=None):
|
||||
if self.file_line_progs is None:
|
||||
l = []
|
||||
for pat in self.file_line_pats:
|
||||
l.append(re.compile(pat, re.IGNORECASE))
|
||||
self.file_line_progs = l
|
||||
# x, y = self.event.x, self.event.y
|
||||
# self.text.mark_set("insert", "@%d,%d" % (x, y))
|
||||
line = self.text.get("insert linestart", "insert lineend")
|
||||
result = self._file_line_helper(line)
|
||||
if not result:
|
||||
# Try the previous line. This is handy e.g. in tracebacks,
|
||||
# where you tend to right-click on the displayed source line
|
||||
line = self.text.get("insert -1line linestart",
|
||||
"insert -1line lineend")
|
||||
result = self._file_line_helper(line)
|
||||
if not result:
|
||||
tkMessageBox.showerror(
|
||||
"No special line",
|
||||
"The line you point at doesn't look like "
|
||||
"a valid file name followed by a line number.",
|
||||
master=self.text)
|
||||
return
|
||||
filename, lineno = result
|
||||
edit = self.flist.open(filename)
|
||||
edit.gotoline(lineno)
|
||||
|
||||
def _file_line_helper(self, line):
|
||||
for prog in self.file_line_progs:
|
||||
m = prog.search(line)
|
||||
if m:
|
||||
break
|
||||
else:
|
||||
return None
|
||||
filename, lineno = m.group(1, 2)
|
||||
try:
|
||||
f = open(filename, "r")
|
||||
f.close()
|
||||
except IOError:
|
||||
return None
|
||||
try:
|
||||
return filename, int(lineno)
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
# These classes are currently not used but might come in handy
|
||||
|
||||
class OnDemandOutputWindow:
|
||||
|
||||
tagdefs = {
|
||||
# XXX Should use IdlePrefs.ColorPrefs
|
||||
"stdout": {"foreground": "blue"},
|
||||
"stderr": {"foreground": "#007700"},
|
||||
}
|
||||
|
||||
def __init__(self, flist):
|
||||
self.flist = flist
|
||||
self.owin = None
|
||||
|
||||
def write(self, s, tags, mark):
|
||||
if not self.owin:
|
||||
self.setup()
|
||||
self.owin.write(s, tags, mark)
|
||||
|
||||
def setup(self):
|
||||
self.owin = owin = OutputWindow(self.flist)
|
||||
text = owin.text
|
||||
for tag, cnf in self.tagdefs.items():
|
||||
if cnf:
|
||||
apply(text.tag_configure, (tag,), cnf)
|
||||
text.tag_raise('sel')
|
||||
self.write = self.owin.write
|
||||
|
||||
class PseudoFile:
|
||||
|
||||
def __init__(self, owin, tags, mark="end"):
|
||||
self.owin = owin
|
||||
self.tags = tags
|
||||
self.mark = mark
|
||||
|
||||
def write(self, s):
|
||||
self.owin.write(s, self.tags, self.mark)
|
||||
|
||||
def writelines(self, l):
|
||||
map(self.write, l)
|
||||
|
||||
def flush(self):
|
||||
pass
|
|
@ -1,189 +0,0 @@
|
|||
"""ParenMatch -- An IDLE extension for parenthesis matching.
|
||||
|
||||
When you hit a right paren, the cursor should move briefly to the left
|
||||
paren. Paren here is used generically; the matching applies to
|
||||
parentheses, square brackets, and curly braces.
|
||||
|
||||
WARNING: This extension will fight with the CallTips extension,
|
||||
because they both are interested in the KeyRelease-parenright event.
|
||||
We'll have to fix IDLE to do something reasonable when two or more
|
||||
extensions what to capture the same event.
|
||||
"""
|
||||
|
||||
import PyParse
|
||||
from AutoIndent import AutoIndent, index2line
|
||||
from IdleConf import idleconf
|
||||
|
||||
class ParenMatch:
|
||||
"""Highlight matching parentheses
|
||||
|
||||
There are three supported style of paren matching, based loosely
|
||||
on the Emacs options. The style is select based on the
|
||||
HILITE_STYLE attribute; it can be changed used the set_style
|
||||
method.
|
||||
|
||||
The supported styles are:
|
||||
|
||||
default -- When a right paren is typed, highlight the matching
|
||||
left paren for 1/2 sec.
|
||||
|
||||
expression -- When a right paren is typed, highlight the entire
|
||||
expression from the left paren to the right paren.
|
||||
|
||||
TODO:
|
||||
- fix interaction with CallTips
|
||||
- extend IDLE with configuration dialog to change options
|
||||
- implement rest of Emacs highlight styles (see below)
|
||||
- print mismatch warning in IDLE status window
|
||||
|
||||
Note: In Emacs, there are several styles of highlight where the
|
||||
matching paren is highlighted whenever the cursor is immediately
|
||||
to the right of a right paren. I don't know how to do that in Tk,
|
||||
so I haven't bothered.
|
||||
"""
|
||||
|
||||
menudefs = []
|
||||
|
||||
keydefs = {
|
||||
'<<flash-open-paren>>' : ('<KeyRelease-parenright>',
|
||||
'<KeyRelease-bracketright>',
|
||||
'<KeyRelease-braceright>'),
|
||||
'<<check-restore>>' : ('<KeyPress>',),
|
||||
}
|
||||
|
||||
windows_keydefs = {}
|
||||
unix_keydefs = {}
|
||||
|
||||
iconf = idleconf.getsection('ParenMatch')
|
||||
STYLE = iconf.getdef('style', 'default')
|
||||
FLASH_DELAY = iconf.getint('flash-delay')
|
||||
HILITE_CONFIG = iconf.getcolor('hilite')
|
||||
BELL = iconf.getboolean('bell')
|
||||
del iconf
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
self.text = editwin.text
|
||||
self.finder = LastOpenBracketFinder(editwin)
|
||||
self.counter = 0
|
||||
self._restore = None
|
||||
self.set_style(self.STYLE)
|
||||
|
||||
def set_style(self, style):
|
||||
self.STYLE = style
|
||||
if style == "default":
|
||||
self.create_tag = self.create_tag_default
|
||||
self.set_timeout = self.set_timeout_last
|
||||
elif style == "expression":
|
||||
self.create_tag = self.create_tag_expression
|
||||
self.set_timeout = self.set_timeout_none
|
||||
|
||||
def flash_open_paren_event(self, event):
|
||||
index = self.finder.find(keysym_type(event.keysym))
|
||||
if index is None:
|
||||
self.warn_mismatched()
|
||||
return
|
||||
self._restore = 1
|
||||
self.create_tag(index)
|
||||
self.set_timeout()
|
||||
|
||||
def check_restore_event(self, event=None):
|
||||
if self._restore:
|
||||
self.text.tag_delete("paren")
|
||||
self._restore = None
|
||||
|
||||
def handle_restore_timer(self, timer_count):
|
||||
if timer_count + 1 == self.counter:
|
||||
self.check_restore_event()
|
||||
|
||||
def warn_mismatched(self):
|
||||
if self.BELL:
|
||||
self.text.bell()
|
||||
|
||||
# any one of the create_tag_XXX methods can be used depending on
|
||||
# the style
|
||||
|
||||
def create_tag_default(self, index):
|
||||
"""Highlight the single paren that matches"""
|
||||
self.text.tag_add("paren", index)
|
||||
self.text.tag_config("paren", self.HILITE_CONFIG)
|
||||
|
||||
def create_tag_expression(self, index):
|
||||
"""Highlight the entire expression"""
|
||||
self.text.tag_add("paren", index, "insert")
|
||||
self.text.tag_config("paren", self.HILITE_CONFIG)
|
||||
|
||||
# any one of the set_timeout_XXX methods can be used depending on
|
||||
# the style
|
||||
|
||||
def set_timeout_none(self):
|
||||
"""Highlight will remain until user input turns it off"""
|
||||
pass
|
||||
|
||||
def set_timeout_last(self):
|
||||
"""The last highlight created will be removed after .5 sec"""
|
||||
# associate a counter with an event; only disable the "paren"
|
||||
# tag if the event is for the most recent timer.
|
||||
self.editwin.text_frame.after(self.FLASH_DELAY,
|
||||
lambda self=self, c=self.counter: \
|
||||
self.handle_restore_timer(c))
|
||||
self.counter = self.counter + 1
|
||||
|
||||
def keysym_type(ks):
|
||||
# Not all possible chars or keysyms are checked because of the
|
||||
# limited context in which the function is used.
|
||||
if ks == "parenright" or ks == "(":
|
||||
return "paren"
|
||||
if ks == "bracketright" or ks == "[":
|
||||
return "bracket"
|
||||
if ks == "braceright" or ks == "{":
|
||||
return "brace"
|
||||
|
||||
class LastOpenBracketFinder:
|
||||
num_context_lines = AutoIndent.num_context_lines
|
||||
indentwidth = AutoIndent.indentwidth
|
||||
tabwidth = AutoIndent.tabwidth
|
||||
context_use_ps1 = AutoIndent.context_use_ps1
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
self.text = editwin.text
|
||||
|
||||
def _find_offset_in_buf(self, lno):
|
||||
y = PyParse.Parser(self.indentwidth, self.tabwidth)
|
||||
for context in self.num_context_lines:
|
||||
startat = max(lno - context, 1)
|
||||
startatindex = `startat` + ".0"
|
||||
# rawtext needs to contain everything up to the last
|
||||
# character, which was the close paren. the parser also
|
||||
# requires that the last line ends with "\n"
|
||||
rawtext = self.text.get(startatindex, "insert")[:-1] + "\n"
|
||||
y.set_str(rawtext)
|
||||
bod = y.find_good_parse_start(
|
||||
self.context_use_ps1,
|
||||
self._build_char_in_string_func(startatindex))
|
||||
if bod is not None or startat == 1:
|
||||
break
|
||||
y.set_lo(bod or 0)
|
||||
i = y.get_last_open_bracket_pos()
|
||||
return i, y.str
|
||||
|
||||
def find(self, right_keysym_type):
|
||||
"""Return the location of the last open paren"""
|
||||
lno = index2line(self.text.index("insert"))
|
||||
i, buf = self._find_offset_in_buf(lno)
|
||||
if i is None \
|
||||
or keysym_type(buf[i]) != right_keysym_type:
|
||||
return None
|
||||
lines_back = buf[i:].count("\n") - 1
|
||||
# subtract one for the "\n" added to please the parser
|
||||
upto_open = buf[:i]
|
||||
j = upto_open.rfind("\n") + 1 # offset of column 0 of line
|
||||
offset = i - j
|
||||
return "%d.%d" % (lno - lines_back, offset)
|
||||
|
||||
def _build_char_in_string_func(self, startindex):
|
||||
def inner(offset, startindex=startindex,
|
||||
icis=self.editwin.is_char_in_string):
|
||||
return icis(startindex + "%dc" % offset)
|
||||
return inner
|
|
@ -1,95 +0,0 @@
|
|||
import os
|
||||
import sys
|
||||
import imp
|
||||
|
||||
from TreeWidget import TreeItem
|
||||
from ClassBrowser import ClassBrowser, ModuleBrowserTreeItem
|
||||
|
||||
class PathBrowser(ClassBrowser):
|
||||
|
||||
def __init__(self, flist):
|
||||
self.init(flist)
|
||||
|
||||
def settitle(self):
|
||||
self.top.wm_title("Path Browser")
|
||||
self.top.wm_iconname("Path Browser")
|
||||
|
||||
def rootnode(self):
|
||||
return PathBrowserTreeItem()
|
||||
|
||||
class PathBrowserTreeItem(TreeItem):
|
||||
|
||||
def GetText(self):
|
||||
return "sys.path"
|
||||
|
||||
def GetSubList(self):
|
||||
sublist = []
|
||||
for dir in sys.path:
|
||||
item = DirBrowserTreeItem(dir)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
class DirBrowserTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, dir, packages=[]):
|
||||
self.dir = dir
|
||||
self.packages = packages
|
||||
|
||||
def GetText(self):
|
||||
if not self.packages:
|
||||
return self.dir
|
||||
else:
|
||||
return self.packages[-1] + ": package"
|
||||
|
||||
def GetSubList(self):
|
||||
try:
|
||||
names = os.listdir(self.dir or os.curdir)
|
||||
except os.error:
|
||||
return []
|
||||
packages = []
|
||||
for name in names:
|
||||
file = os.path.join(self.dir, name)
|
||||
if self.ispackagedir(file):
|
||||
nn = os.path.normcase(name)
|
||||
packages.append((nn, name, file))
|
||||
packages.sort()
|
||||
sublist = []
|
||||
for nn, name, file in packages:
|
||||
item = DirBrowserTreeItem(file, self.packages + [name])
|
||||
sublist.append(item)
|
||||
for nn, name in self.listmodules(names):
|
||||
item = ModuleBrowserTreeItem(os.path.join(self.dir, name))
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
def ispackagedir(self, file):
|
||||
if not os.path.isdir(file):
|
||||
return 0
|
||||
init = os.path.join(file, "__init__.py")
|
||||
return os.path.exists(init)
|
||||
|
||||
def listmodules(self, allnames):
|
||||
modules = {}
|
||||
suffixes = imp.get_suffixes()
|
||||
sorted = []
|
||||
for suff, mode, flag in suffixes:
|
||||
i = -len(suff)
|
||||
for name in allnames[:]:
|
||||
normed_name = os.path.normcase(name)
|
||||
if normed_name[i:] == suff:
|
||||
mod_name = name[:i]
|
||||
if not modules.has_key(mod_name):
|
||||
modules[mod_name] = None
|
||||
sorted.append((normed_name, name))
|
||||
allnames.remove(name)
|
||||
sorted.sort()
|
||||
return sorted
|
||||
|
||||
def main():
|
||||
import PyShell
|
||||
PathBrowser(PyShell.flist)
|
||||
if sys.stdin is sys.__stdin__:
|
||||
mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,85 +0,0 @@
|
|||
from WidgetRedirector import WidgetRedirector
|
||||
from Delegator import Delegator
|
||||
|
||||
class Percolator:
|
||||
|
||||
def __init__(self, text):
|
||||
# XXX would be nice to inherit from Delegator
|
||||
self.text = text
|
||||
self.redir = WidgetRedirector(text)
|
||||
self.top = self.bottom = Delegator(text)
|
||||
self.bottom.insert = self.redir.register("insert", self.insert)
|
||||
self.bottom.delete = self.redir.register("delete", self.delete)
|
||||
self.filters = []
|
||||
|
||||
def close(self):
|
||||
while self.top is not self.bottom:
|
||||
self.removefilter(self.top)
|
||||
self.top = None
|
||||
self.bottom.setdelegate(None); self.bottom = None
|
||||
self.redir.close(); self.redir = None
|
||||
self.text = None
|
||||
|
||||
def insert(self, index, chars, tags=None):
|
||||
# Could go away if inheriting from Delegator
|
||||
self.top.insert(index, chars, tags)
|
||||
|
||||
def delete(self, index1, index2=None):
|
||||
# Could go away if inheriting from Delegator
|
||||
self.top.delete(index1, index2)
|
||||
|
||||
def insertfilter(self, filter):
|
||||
# Perhaps rename to pushfilter()?
|
||||
assert isinstance(filter, Delegator)
|
||||
assert filter.delegate is None
|
||||
filter.setdelegate(self.top)
|
||||
self.top = filter
|
||||
|
||||
def removefilter(self, filter):
|
||||
# XXX Perhaps should only support popfilter()?
|
||||
assert isinstance(filter, Delegator)
|
||||
assert filter.delegate is not None
|
||||
f = self.top
|
||||
if f is filter:
|
||||
self.top = filter.delegate
|
||||
filter.setdelegate(None)
|
||||
else:
|
||||
while f.delegate is not filter:
|
||||
assert f is not self.bottom
|
||||
f.resetcache()
|
||||
f = f.delegate
|
||||
f.setdelegate(filter.delegate)
|
||||
filter.setdelegate(None)
|
||||
|
||||
|
||||
def main():
|
||||
class Tracer(Delegator):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
Delegator.__init__(self, None)
|
||||
def insert(self, *args):
|
||||
print self.name, ": insert", args
|
||||
apply(self.delegate.insert, args)
|
||||
def delete(self, *args):
|
||||
print self.name, ": delete", args
|
||||
apply(self.delegate.delete, args)
|
||||
root = Tk()
|
||||
root.wm_protocol("WM_DELETE_WINDOW", root.quit)
|
||||
text = Text()
|
||||
text.pack()
|
||||
text.focus_set()
|
||||
p = Percolator(text)
|
||||
t1 = Tracer("t1")
|
||||
t2 = Tracer("t2")
|
||||
p.insertfilter(t1)
|
||||
p.insertfilter(t2)
|
||||
root.mainloop()
|
||||
p.removefilter(t2)
|
||||
root.mainloop()
|
||||
p.insertfilter(t2)
|
||||
p.removefilter(t1)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
from Tkinter import *
|
||||
main()
|
|
@ -1,584 +0,0 @@
|
|||
import re
|
||||
import sys
|
||||
|
||||
# Reason last stmt is continued (or C_NONE if it's not).
|
||||
C_NONE, C_BACKSLASH, C_STRING, C_BRACKET = range(4)
|
||||
|
||||
if 0: # for throwaway debugging output
|
||||
def dump(*stuff):
|
||||
sys.__stdout__.write(" ".join(map(str, stuff)) + "\n")
|
||||
|
||||
# Find what looks like the start of a popular stmt.
|
||||
|
||||
_synchre = re.compile(r"""
|
||||
^
|
||||
[ \t]*
|
||||
(?: if
|
||||
| for
|
||||
| while
|
||||
| else
|
||||
| def
|
||||
| return
|
||||
| assert
|
||||
| break
|
||||
| class
|
||||
| continue
|
||||
| elif
|
||||
| try
|
||||
| except
|
||||
| raise
|
||||
| import
|
||||
| yield
|
||||
)
|
||||
\b
|
||||
""", re.VERBOSE | re.MULTILINE).search
|
||||
|
||||
# Match blank line or non-indenting comment line.
|
||||
|
||||
_junkre = re.compile(r"""
|
||||
[ \t]*
|
||||
(?: \# \S .* )?
|
||||
\n
|
||||
""", re.VERBOSE).match
|
||||
|
||||
# Match any flavor of string; the terminating quote is optional
|
||||
# so that we're robust in the face of incomplete program text.
|
||||
|
||||
_match_stringre = re.compile(r"""
|
||||
\""" [^"\\]* (?:
|
||||
(?: \\. | "(?!"") )
|
||||
[^"\\]*
|
||||
)*
|
||||
(?: \""" )?
|
||||
|
||||
| " [^"\\\n]* (?: \\. [^"\\\n]* )* "?
|
||||
|
||||
| ''' [^'\\]* (?:
|
||||
(?: \\. | '(?!'') )
|
||||
[^'\\]*
|
||||
)*
|
||||
(?: ''' )?
|
||||
|
||||
| ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
|
||||
""", re.VERBOSE | re.DOTALL).match
|
||||
|
||||
# Match a line that starts with something interesting;
|
||||
# used to find the first item of a bracket structure.
|
||||
|
||||
_itemre = re.compile(r"""
|
||||
[ \t]*
|
||||
[^\s#\\] # if we match, m.end()-1 is the interesting char
|
||||
""", re.VERBOSE).match
|
||||
|
||||
# Match start of stmts that should be followed by a dedent.
|
||||
|
||||
_closere = re.compile(r"""
|
||||
\s*
|
||||
(?: return
|
||||
| break
|
||||
| continue
|
||||
| raise
|
||||
| pass
|
||||
)
|
||||
\b
|
||||
""", re.VERBOSE).match
|
||||
|
||||
# Chew up non-special chars as quickly as possible. If match is
|
||||
# successful, m.end() less 1 is the index of the last boring char
|
||||
# matched. If match is unsuccessful, the string starts with an
|
||||
# interesting char.
|
||||
|
||||
_chew_ordinaryre = re.compile(r"""
|
||||
[^[\](){}#'"\\]+
|
||||
""", re.VERBOSE).match
|
||||
|
||||
# Build translation table to map uninteresting chars to "x", open
|
||||
# brackets to "(", and close brackets to ")".
|
||||
|
||||
_tran = ['x'] * 256
|
||||
for ch in "({[":
|
||||
_tran[ord(ch)] = '('
|
||||
for ch in ")}]":
|
||||
_tran[ord(ch)] = ')'
|
||||
for ch in "\"'\\\n#":
|
||||
_tran[ord(ch)] = ch
|
||||
_tran = ''.join(_tran)
|
||||
del ch
|
||||
|
||||
try:
|
||||
UnicodeType = type(unicode(""))
|
||||
except NameError:
|
||||
UnicodeType = None
|
||||
|
||||
class Parser:
|
||||
|
||||
def __init__(self, indentwidth, tabwidth):
|
||||
self.indentwidth = indentwidth
|
||||
self.tabwidth = tabwidth
|
||||
|
||||
def set_str(self, str):
|
||||
assert len(str) == 0 or str[-1] == '\n'
|
||||
if type(str) is UnicodeType:
|
||||
# The parse functions have no idea what to do with Unicode, so
|
||||
# replace all Unicode characters with "x". This is "safe"
|
||||
# so long as the only characters germane to parsing the structure
|
||||
# of Python are 7-bit ASCII. It's *necessary* because Unicode
|
||||
# strings don't have a .translate() method that supports
|
||||
# deletechars.
|
||||
uniphooey = str
|
||||
str = []
|
||||
push = str.append
|
||||
for raw in map(ord, uniphooey):
|
||||
push(raw < 127 and chr(raw) or "x")
|
||||
str = "".join(str)
|
||||
self.str = str
|
||||
self.study_level = 0
|
||||
|
||||
# Return index of a good place to begin parsing, as close to the
|
||||
# end of the string as possible. This will be the start of some
|
||||
# popular stmt like "if" or "def". Return None if none found:
|
||||
# the caller should pass more prior context then, if possible, or
|
||||
# if not (the entire program text up until the point of interest
|
||||
# has already been tried) pass 0 to set_lo.
|
||||
#
|
||||
# This will be reliable iff given a reliable is_char_in_string
|
||||
# function, meaning that when it says "no", it's absolutely
|
||||
# guaranteed that the char is not in a string.
|
||||
#
|
||||
# Ack, hack: in the shell window this kills us, because there's
|
||||
# no way to tell the differences between output, >>> etc and
|
||||
# user input. Indeed, IDLE's first output line makes the rest
|
||||
# look like it's in an unclosed paren!:
|
||||
# Python 1.5.2 (#0, Apr 13 1999, ...
|
||||
|
||||
def find_good_parse_start(self, use_ps1, is_char_in_string=None,
|
||||
_synchre=_synchre):
|
||||
str, pos = self.str, None
|
||||
if use_ps1:
|
||||
# shell window
|
||||
ps1 = '\n' + sys.ps1
|
||||
i = str.rfind(ps1)
|
||||
if i >= 0:
|
||||
pos = i + len(ps1)
|
||||
# make it look like there's a newline instead
|
||||
# of ps1 at the start -- hacking here once avoids
|
||||
# repeated hackery later
|
||||
self.str = str[:pos-1] + '\n' + str[pos:]
|
||||
return pos
|
||||
|
||||
# File window -- real work.
|
||||
if not is_char_in_string:
|
||||
# no clue -- make the caller pass everything
|
||||
return None
|
||||
|
||||
# Peek back from the end for a good place to start,
|
||||
# but don't try too often; pos will be left None, or
|
||||
# bumped to a legitimate synch point.
|
||||
limit = len(str)
|
||||
for tries in range(5):
|
||||
i = str.rfind(":\n", 0, limit)
|
||||
if i < 0:
|
||||
break
|
||||
i = str.rfind('\n', 0, i) + 1 # start of colon line
|
||||
m = _synchre(str, i, limit)
|
||||
if m and not is_char_in_string(m.start()):
|
||||
pos = m.start()
|
||||
break
|
||||
limit = i
|
||||
if pos is None:
|
||||
# Nothing looks like a block-opener, or stuff does
|
||||
# but is_char_in_string keeps returning true; most likely
|
||||
# we're in or near a giant string, the colorizer hasn't
|
||||
# caught up enough to be helpful, or there simply *aren't*
|
||||
# any interesting stmts. In any of these cases we're
|
||||
# going to have to parse the whole thing to be sure, so
|
||||
# give it one last try from the start, but stop wasting
|
||||
# time here regardless of the outcome.
|
||||
m = _synchre(str)
|
||||
if m and not is_char_in_string(m.start()):
|
||||
pos = m.start()
|
||||
return pos
|
||||
|
||||
# Peeking back worked; look forward until _synchre no longer
|
||||
# matches.
|
||||
i = pos + 1
|
||||
while 1:
|
||||
m = _synchre(str, i)
|
||||
if m:
|
||||
s, i = m.span()
|
||||
if not is_char_in_string(s):
|
||||
pos = s
|
||||
else:
|
||||
break
|
||||
return pos
|
||||
|
||||
# Throw away the start of the string. Intended to be called with
|
||||
# find_good_parse_start's result.
|
||||
|
||||
def set_lo(self, lo):
|
||||
assert lo == 0 or self.str[lo-1] == '\n'
|
||||
if lo > 0:
|
||||
self.str = self.str[lo:]
|
||||
|
||||
# As quickly as humanly possible <wink>, find the line numbers (0-
|
||||
# based) of the non-continuation lines.
|
||||
# Creates self.{goodlines, continuation}.
|
||||
|
||||
def _study1(self):
|
||||
if self.study_level >= 1:
|
||||
return
|
||||
self.study_level = 1
|
||||
|
||||
# Map all uninteresting characters to "x", all open brackets
|
||||
# to "(", all close brackets to ")", then collapse runs of
|
||||
# uninteresting characters. This can cut the number of chars
|
||||
# by a factor of 10-40, and so greatly speed the following loop.
|
||||
str = self.str
|
||||
str = str.translate(_tran)
|
||||
str = str.replace('xxxxxxxx', 'x')
|
||||
str = str.replace('xxxx', 'x')
|
||||
str = str.replace('xx', 'x')
|
||||
str = str.replace('xx', 'x')
|
||||
str = str.replace('\nx', '\n')
|
||||
# note that replacing x\n with \n would be incorrect, because
|
||||
# x may be preceded by a backslash
|
||||
|
||||
# March over the squashed version of the program, accumulating
|
||||
# the line numbers of non-continued stmts, and determining
|
||||
# whether & why the last stmt is a continuation.
|
||||
continuation = C_NONE
|
||||
level = lno = 0 # level is nesting level; lno is line number
|
||||
self.goodlines = goodlines = [0]
|
||||
push_good = goodlines.append
|
||||
i, n = 0, len(str)
|
||||
while i < n:
|
||||
ch = str[i]
|
||||
i = i+1
|
||||
|
||||
# cases are checked in decreasing order of frequency
|
||||
if ch == 'x':
|
||||
continue
|
||||
|
||||
if ch == '\n':
|
||||
lno = lno + 1
|
||||
if level == 0:
|
||||
push_good(lno)
|
||||
# else we're in an unclosed bracket structure
|
||||
continue
|
||||
|
||||
if ch == '(':
|
||||
level = level + 1
|
||||
continue
|
||||
|
||||
if ch == ')':
|
||||
if level:
|
||||
level = level - 1
|
||||
# else the program is invalid, but we can't complain
|
||||
continue
|
||||
|
||||
if ch == '"' or ch == "'":
|
||||
# consume the string
|
||||
quote = ch
|
||||
if str[i-1:i+2] == quote * 3:
|
||||
quote = quote * 3
|
||||
w = len(quote) - 1
|
||||
i = i+w
|
||||
while i < n:
|
||||
ch = str[i]
|
||||
i = i+1
|
||||
|
||||
if ch == 'x':
|
||||
continue
|
||||
|
||||
if str[i-1:i+w] == quote:
|
||||
i = i+w
|
||||
break
|
||||
|
||||
if ch == '\n':
|
||||
lno = lno + 1
|
||||
if w == 0:
|
||||
# unterminated single-quoted string
|
||||
if level == 0:
|
||||
push_good(lno)
|
||||
break
|
||||
continue
|
||||
|
||||
if ch == '\\':
|
||||
assert i < n
|
||||
if str[i] == '\n':
|
||||
lno = lno + 1
|
||||
i = i+1
|
||||
continue
|
||||
|
||||
# else comment char or paren inside string
|
||||
|
||||
else:
|
||||
# didn't break out of the loop, so we're still
|
||||
# inside a string
|
||||
continuation = C_STRING
|
||||
continue # with outer loop
|
||||
|
||||
if ch == '#':
|
||||
# consume the comment
|
||||
i = str.find('\n', i)
|
||||
assert i >= 0
|
||||
continue
|
||||
|
||||
assert ch == '\\'
|
||||
assert i < n
|
||||
if str[i] == '\n':
|
||||
lno = lno + 1
|
||||
if i+1 == n:
|
||||
continuation = C_BACKSLASH
|
||||
i = i+1
|
||||
|
||||
# The last stmt may be continued for all 3 reasons.
|
||||
# String continuation takes precedence over bracket
|
||||
# continuation, which beats backslash continuation.
|
||||
if continuation != C_STRING and level > 0:
|
||||
continuation = C_BRACKET
|
||||
self.continuation = continuation
|
||||
|
||||
# Push the final line number as a sentinel value, regardless of
|
||||
# whether it's continued.
|
||||
assert (continuation == C_NONE) == (goodlines[-1] == lno)
|
||||
if goodlines[-1] != lno:
|
||||
push_good(lno)
|
||||
|
||||
def get_continuation_type(self):
|
||||
self._study1()
|
||||
return self.continuation
|
||||
|
||||
# study1 was sufficient to determine the continuation status,
|
||||
# but doing more requires looking at every character. study2
|
||||
# does this for the last interesting statement in the block.
|
||||
# Creates:
|
||||
# self.stmt_start, stmt_end
|
||||
# slice indices of last interesting stmt
|
||||
# self.lastch
|
||||
# last non-whitespace character before optional trailing
|
||||
# comment
|
||||
# self.lastopenbracketpos
|
||||
# if continuation is C_BRACKET, index of last open bracket
|
||||
|
||||
def _study2(self):
|
||||
if self.study_level >= 2:
|
||||
return
|
||||
self._study1()
|
||||
self.study_level = 2
|
||||
|
||||
# Set p and q to slice indices of last interesting stmt.
|
||||
str, goodlines = self.str, self.goodlines
|
||||
i = len(goodlines) - 1
|
||||
p = len(str) # index of newest line
|
||||
while i:
|
||||
assert p
|
||||
# p is the index of the stmt at line number goodlines[i].
|
||||
# Move p back to the stmt at line number goodlines[i-1].
|
||||
q = p
|
||||
for nothing in range(goodlines[i-1], goodlines[i]):
|
||||
# tricky: sets p to 0 if no preceding newline
|
||||
p = str.rfind('\n', 0, p-1) + 1
|
||||
# The stmt str[p:q] isn't a continuation, but may be blank
|
||||
# or a non-indenting comment line.
|
||||
if _junkre(str, p):
|
||||
i = i-1
|
||||
else:
|
||||
break
|
||||
if i == 0:
|
||||
# nothing but junk!
|
||||
assert p == 0
|
||||
q = p
|
||||
self.stmt_start, self.stmt_end = p, q
|
||||
|
||||
# Analyze this stmt, to find the last open bracket (if any)
|
||||
# and last interesting character (if any).
|
||||
lastch = ""
|
||||
stack = [] # stack of open bracket indices
|
||||
push_stack = stack.append
|
||||
while p < q:
|
||||
# suck up all except ()[]{}'"#\\
|
||||
m = _chew_ordinaryre(str, p, q)
|
||||
if m:
|
||||
# we skipped at least one boring char
|
||||
newp = m.end()
|
||||
# back up over totally boring whitespace
|
||||
i = newp - 1 # index of last boring char
|
||||
while i >= p and str[i] in " \t\n":
|
||||
i = i-1
|
||||
if i >= p:
|
||||
lastch = str[i]
|
||||
p = newp
|
||||
if p >= q:
|
||||
break
|
||||
|
||||
ch = str[p]
|
||||
|
||||
if ch in "([{":
|
||||
push_stack(p)
|
||||
lastch = ch
|
||||
p = p+1
|
||||
continue
|
||||
|
||||
if ch in ")]}":
|
||||
if stack:
|
||||
del stack[-1]
|
||||
lastch = ch
|
||||
p = p+1
|
||||
continue
|
||||
|
||||
if ch == '"' or ch == "'":
|
||||
# consume string
|
||||
# Note that study1 did this with a Python loop, but
|
||||
# we use a regexp here; the reason is speed in both
|
||||
# cases; the string may be huge, but study1 pre-squashed
|
||||
# strings to a couple of characters per line. study1
|
||||
# also needed to keep track of newlines, and we don't
|
||||
# have to.
|
||||
lastch = ch
|
||||
p = _match_stringre(str, p, q).end()
|
||||
continue
|
||||
|
||||
if ch == '#':
|
||||
# consume comment and trailing newline
|
||||
p = str.find('\n', p, q) + 1
|
||||
assert p > 0
|
||||
continue
|
||||
|
||||
assert ch == '\\'
|
||||
p = p+1 # beyond backslash
|
||||
assert p < q
|
||||
if str[p] != '\n':
|
||||
# the program is invalid, but can't complain
|
||||
lastch = ch + str[p]
|
||||
p = p+1 # beyond escaped char
|
||||
|
||||
# end while p < q:
|
||||
|
||||
self.lastch = lastch
|
||||
if stack:
|
||||
self.lastopenbracketpos = stack[-1]
|
||||
|
||||
# Assuming continuation is C_BRACKET, return the number
|
||||
# of spaces the next line should be indented.
|
||||
|
||||
def compute_bracket_indent(self):
|
||||
self._study2()
|
||||
assert self.continuation == C_BRACKET
|
||||
j = self.lastopenbracketpos
|
||||
str = self.str
|
||||
n = len(str)
|
||||
origi = i = str.rfind('\n', 0, j) + 1
|
||||
j = j+1 # one beyond open bracket
|
||||
# find first list item; set i to start of its line
|
||||
while j < n:
|
||||
m = _itemre(str, j)
|
||||
if m:
|
||||
j = m.end() - 1 # index of first interesting char
|
||||
extra = 0
|
||||
break
|
||||
else:
|
||||
# this line is junk; advance to next line
|
||||
i = j = str.find('\n', j) + 1
|
||||
else:
|
||||
# nothing interesting follows the bracket;
|
||||
# reproduce the bracket line's indentation + a level
|
||||
j = i = origi
|
||||
while str[j] in " \t":
|
||||
j = j+1
|
||||
extra = self.indentwidth
|
||||
return len(str[i:j].expandtabs(self.tabwidth)) + extra
|
||||
|
||||
# Return number of physical lines in last stmt (whether or not
|
||||
# it's an interesting stmt! this is intended to be called when
|
||||
# continuation is C_BACKSLASH).
|
||||
|
||||
def get_num_lines_in_stmt(self):
|
||||
self._study1()
|
||||
goodlines = self.goodlines
|
||||
return goodlines[-1] - goodlines[-2]
|
||||
|
||||
# Assuming continuation is C_BACKSLASH, return the number of spaces
|
||||
# the next line should be indented. Also assuming the new line is
|
||||
# the first one following the initial line of the stmt.
|
||||
|
||||
def compute_backslash_indent(self):
|
||||
self._study2()
|
||||
assert self.continuation == C_BACKSLASH
|
||||
str = self.str
|
||||
i = self.stmt_start
|
||||
while str[i] in " \t":
|
||||
i = i+1
|
||||
startpos = i
|
||||
|
||||
# See whether the initial line starts an assignment stmt; i.e.,
|
||||
# look for an = operator
|
||||
endpos = str.find('\n', startpos) + 1
|
||||
found = level = 0
|
||||
while i < endpos:
|
||||
ch = str[i]
|
||||
if ch in "([{":
|
||||
level = level + 1
|
||||
i = i+1
|
||||
elif ch in ")]}":
|
||||
if level:
|
||||
level = level - 1
|
||||
i = i+1
|
||||
elif ch == '"' or ch == "'":
|
||||
i = _match_stringre(str, i, endpos).end()
|
||||
elif ch == '#':
|
||||
break
|
||||
elif level == 0 and ch == '=' and \
|
||||
(i == 0 or str[i-1] not in "=<>!") and \
|
||||
str[i+1] != '=':
|
||||
found = 1
|
||||
break
|
||||
else:
|
||||
i = i+1
|
||||
|
||||
if found:
|
||||
# found a legit =, but it may be the last interesting
|
||||
# thing on the line
|
||||
i = i+1 # move beyond the =
|
||||
found = re.match(r"\s*\\", str[i:endpos]) is None
|
||||
|
||||
if not found:
|
||||
# oh well ... settle for moving beyond the first chunk
|
||||
# of non-whitespace chars
|
||||
i = startpos
|
||||
while str[i] not in " \t\n":
|
||||
i = i+1
|
||||
|
||||
return len(str[self.stmt_start:i].expandtabs(\
|
||||
self.tabwidth)) + 1
|
||||
|
||||
# Return the leading whitespace on the initial line of the last
|
||||
# interesting stmt.
|
||||
|
||||
def get_base_indent_string(self):
|
||||
self._study2()
|
||||
i, n = self.stmt_start, self.stmt_end
|
||||
j = i
|
||||
str = self.str
|
||||
while j < n and str[j] in " \t":
|
||||
j = j + 1
|
||||
return str[i:j]
|
||||
|
||||
# Did the last interesting stmt open a block?
|
||||
|
||||
def is_block_opener(self):
|
||||
self._study2()
|
||||
return self.lastch == ':'
|
||||
|
||||
# Did the last interesting stmt close a block?
|
||||
|
||||
def is_block_closer(self):
|
||||
self._study2()
|
||||
return _closere(self.str, self.stmt_start) is not None
|
||||
|
||||
# index of last open bracket ({[, or None if none
|
||||
lastopenbracketpos = None
|
||||
|
||||
def get_last_open_bracket_pos(self):
|
||||
self._study2()
|
||||
return self.lastopenbracketpos
|
|
@ -1,794 +0,0 @@
|
|||
#! /usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import string
|
||||
import getopt
|
||||
import re
|
||||
import warnings
|
||||
import types
|
||||
|
||||
import linecache
|
||||
from code import InteractiveInterpreter
|
||||
|
||||
from Tkinter import *
|
||||
import tkMessageBox
|
||||
|
||||
from EditorWindow import EditorWindow, fixwordbreaks
|
||||
from FileList import FileList
|
||||
from ColorDelegator import ColorDelegator
|
||||
from UndoDelegator import UndoDelegator
|
||||
from OutputWindow import OutputWindow
|
||||
from IdleConf import idleconf
|
||||
import idlever
|
||||
|
||||
# We need to patch linecache.checkcache, because we don't want it
|
||||
# to throw away our <pyshell#...> entries.
|
||||
# Rather than repeating its code here, we save those entries,
|
||||
# then call the original function, and then restore the saved entries.
|
||||
def linecache_checkcache(orig_checkcache=linecache.checkcache):
|
||||
cache = linecache.cache
|
||||
save = {}
|
||||
for filename in cache.keys():
|
||||
if filename[:1] + filename[-1:] == '<>':
|
||||
save[filename] = cache[filename]
|
||||
orig_checkcache()
|
||||
cache.update(save)
|
||||
linecache.checkcache = linecache_checkcache
|
||||
|
||||
|
||||
IDENTCHARS = string.ascii_letters + string.digits + "_"
|
||||
|
||||
|
||||
# Note: <<newline-and-indent>> event is defined in AutoIndent.py
|
||||
|
||||
#$ event <<plain-newline-and-indent>>
|
||||
#$ win <Control-j>
|
||||
#$ unix <Control-j>
|
||||
|
||||
#$ event <<beginning-of-line>>
|
||||
#$ win <Control-a>
|
||||
#$ win <Home>
|
||||
#$ unix <Control-a>
|
||||
#$ unix <Home>
|
||||
|
||||
#$ event <<history-next>>
|
||||
#$ win <Alt-n>
|
||||
#$ unix <Alt-n>
|
||||
|
||||
#$ event <<history-previous>>
|
||||
#$ win <Alt-p>
|
||||
#$ unix <Alt-p>
|
||||
|
||||
#$ event <<interrupt-execution>>
|
||||
#$ win <Control-c>
|
||||
#$ unix <Control-c>
|
||||
|
||||
#$ event <<end-of-file>>
|
||||
#$ win <Control-d>
|
||||
#$ unix <Control-d>
|
||||
|
||||
#$ event <<open-stack-viewer>>
|
||||
|
||||
#$ event <<toggle-debugger>>
|
||||
|
||||
|
||||
class PyShellEditorWindow(EditorWindow):
|
||||
|
||||
# Regular text edit window when a shell is present
|
||||
# XXX ought to merge with regular editor window
|
||||
runnable = True # Shell not present, enable Import Module and Run Script
|
||||
|
||||
def __init__(self, *args):
|
||||
apply(EditorWindow.__init__, (self,) + args)
|
||||
self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)
|
||||
self.text.bind("<<open-python-shell>>", self.flist.open_shell)
|
||||
|
||||
rmenu_specs = [
|
||||
("Set breakpoint here", "<<set-breakpoint-here>>"),
|
||||
]
|
||||
|
||||
def set_breakpoint_here(self, event=None):
|
||||
if not self.flist.pyshell or not self.flist.pyshell.interp.debugger:
|
||||
self.text.bell()
|
||||
return
|
||||
self.flist.pyshell.interp.debugger.set_breakpoint_here(self)
|
||||
|
||||
|
||||
class PyShellFileList(FileList):
|
||||
|
||||
# File list when a shell is present
|
||||
|
||||
EditorWindow = PyShellEditorWindow
|
||||
|
||||
pyshell = None
|
||||
|
||||
def open_shell(self, event=None):
|
||||
if self.pyshell:
|
||||
self.pyshell.wakeup()
|
||||
else:
|
||||
self.pyshell = PyShell(self)
|
||||
self.pyshell.begin()
|
||||
return self.pyshell
|
||||
|
||||
|
||||
class ModifiedColorDelegator(ColorDelegator):
|
||||
|
||||
# Colorizer for the shell window itself
|
||||
|
||||
def recolorize_main(self):
|
||||
self.tag_remove("TODO", "1.0", "iomark")
|
||||
self.tag_add("SYNC", "1.0", "iomark")
|
||||
ColorDelegator.recolorize_main(self)
|
||||
|
||||
tagdefs = ColorDelegator.tagdefs.copy()
|
||||
cconf = idleconf.getsection('Colors')
|
||||
|
||||
tagdefs.update({
|
||||
"stdin": cconf.getcolor("stdin"),
|
||||
"stdout": cconf.getcolor("stdout"),
|
||||
"stderr": cconf.getcolor("stderr"),
|
||||
"console": cconf.getcolor("console"),
|
||||
"ERROR": cconf.getcolor("ERROR"),
|
||||
None: cconf.getcolor("normal"),
|
||||
})
|
||||
|
||||
|
||||
class ModifiedUndoDelegator(UndoDelegator):
|
||||
|
||||
# Forbid insert/delete before the I/O mark
|
||||
|
||||
def insert(self, index, chars, tags=None):
|
||||
try:
|
||||
if self.delegate.compare(index, "<", "iomark"):
|
||||
self.delegate.bell()
|
||||
return
|
||||
except TclError:
|
||||
pass
|
||||
UndoDelegator.insert(self, index, chars, tags)
|
||||
|
||||
def delete(self, index1, index2=None):
|
||||
try:
|
||||
if self.delegate.compare(index1, "<", "iomark"):
|
||||
self.delegate.bell()
|
||||
return
|
||||
except TclError:
|
||||
pass
|
||||
UndoDelegator.delete(self, index1, index2)
|
||||
|
||||
class ModifiedInterpreter(InteractiveInterpreter):
|
||||
|
||||
def __init__(self, tkconsole):
|
||||
self.tkconsole = tkconsole
|
||||
locals = sys.modules['__main__'].__dict__
|
||||
InteractiveInterpreter.__init__(self, locals=locals)
|
||||
self.save_warnings_filters = None
|
||||
|
||||
gid = 0
|
||||
|
||||
def execsource(self, source):
|
||||
# Like runsource() but assumes complete exec source
|
||||
filename = self.stuffsource(source)
|
||||
self.execfile(filename, source)
|
||||
|
||||
def execfile(self, filename, source=None):
|
||||
# Execute an existing file
|
||||
if source is None:
|
||||
source = open(filename, "r").read()
|
||||
try:
|
||||
code = compile(source, filename, "exec")
|
||||
except (OverflowError, SyntaxError):
|
||||
self.tkconsole.resetoutput()
|
||||
InteractiveInterpreter.showsyntaxerror(self, filename)
|
||||
else:
|
||||
self.runcode(code)
|
||||
|
||||
def runsource(self, source):
|
||||
# Extend base class to stuff the source in the line cache first
|
||||
filename = self.stuffsource(source)
|
||||
self.more = 0
|
||||
self.save_warnings_filters = warnings.filters[:]
|
||||
warnings.filterwarnings(action="error", category=SyntaxWarning)
|
||||
if isinstance(source, types.UnicodeType):
|
||||
import IOBinding
|
||||
try:
|
||||
source = source.encode(IOBinding.encoding)
|
||||
except UnicodeError:
|
||||
self.tkconsole.resetoutput()
|
||||
self.write("Unsupported characters in input")
|
||||
return
|
||||
try:
|
||||
return InteractiveInterpreter.runsource(self, source, filename)
|
||||
finally:
|
||||
if self.save_warnings_filters is not None:
|
||||
warnings.filters[:] = self.save_warnings_filters
|
||||
self.save_warnings_filters = None
|
||||
|
||||
def stuffsource(self, source):
|
||||
# Stuff source in the filename cache
|
||||
filename = "<pyshell#%d>" % self.gid
|
||||
self.gid = self.gid + 1
|
||||
lines = source.split("\n")
|
||||
linecache.cache[filename] = len(source)+1, 0, lines, filename
|
||||
return filename
|
||||
|
||||
def showsyntaxerror(self, filename=None):
|
||||
# Extend base class to color the offending position
|
||||
# (instead of printing it and pointing at it with a caret)
|
||||
text = self.tkconsole.text
|
||||
stuff = self.unpackerror()
|
||||
if not stuff:
|
||||
self.tkconsole.resetoutput()
|
||||
InteractiveInterpreter.showsyntaxerror(self, filename)
|
||||
return
|
||||
msg, lineno, offset, line = stuff
|
||||
if lineno == 1:
|
||||
pos = "iomark + %d chars" % (offset-1)
|
||||
else:
|
||||
pos = "iomark linestart + %d lines + %d chars" % (lineno-1,
|
||||
offset-1)
|
||||
text.tag_add("ERROR", pos)
|
||||
text.see(pos)
|
||||
char = text.get(pos)
|
||||
if char and char in IDENTCHARS:
|
||||
text.tag_add("ERROR", pos + " wordstart", pos)
|
||||
self.tkconsole.resetoutput()
|
||||
self.write("SyntaxError: %s\n" % str(msg))
|
||||
|
||||
def unpackerror(self):
|
||||
type, value, tb = sys.exc_info()
|
||||
ok = type is SyntaxError
|
||||
if ok:
|
||||
try:
|
||||
msg, (dummy_filename, lineno, offset, line) = value
|
||||
except:
|
||||
ok = 0
|
||||
if ok:
|
||||
return msg, lineno, offset, line
|
||||
else:
|
||||
return None
|
||||
|
||||
def showtraceback(self):
|
||||
# Extend base class method to reset output properly
|
||||
self.tkconsole.resetoutput()
|
||||
self.checklinecache()
|
||||
InteractiveInterpreter.showtraceback(self)
|
||||
|
||||
def checklinecache(self):
|
||||
c = linecache.cache
|
||||
for key in c.keys():
|
||||
if key[:1] + key[-1:] != "<>":
|
||||
del c[key]
|
||||
|
||||
debugger = None
|
||||
|
||||
def setdebugger(self, debugger):
|
||||
self.debugger = debugger
|
||||
|
||||
def getdebugger(self):
|
||||
return self.debugger
|
||||
|
||||
def runcode(self, code):
|
||||
# Override base class method
|
||||
if self.save_warnings_filters is not None:
|
||||
warnings.filters[:] = self.save_warnings_filters
|
||||
self.save_warnings_filters = None
|
||||
debugger = self.debugger
|
||||
try:
|
||||
self.tkconsole.beginexecuting()
|
||||
try:
|
||||
if debugger:
|
||||
debugger.run(code, self.locals)
|
||||
else:
|
||||
exec code in self.locals
|
||||
except SystemExit:
|
||||
if tkMessageBox.askyesno(
|
||||
"Exit?",
|
||||
"Do you want to exit altogether?",
|
||||
default="yes",
|
||||
master=self.tkconsole.text):
|
||||
raise
|
||||
else:
|
||||
self.showtraceback()
|
||||
if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
|
||||
self.tkconsole.open_stack_viewer()
|
||||
except:
|
||||
self.showtraceback()
|
||||
if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
|
||||
self.tkconsole.open_stack_viewer()
|
||||
|
||||
finally:
|
||||
self.tkconsole.endexecuting()
|
||||
|
||||
def write(self, s):
|
||||
# Override base class write
|
||||
self.tkconsole.console.write(s)
|
||||
|
||||
|
||||
class PyShell(OutputWindow):
|
||||
|
||||
shell_title = "Python Shell"
|
||||
|
||||
# Override classes
|
||||
ColorDelegator = ModifiedColorDelegator
|
||||
UndoDelegator = ModifiedUndoDelegator
|
||||
|
||||
# Override menu bar specs
|
||||
menu_specs = PyShellEditorWindow.menu_specs[:]
|
||||
menu_specs.insert(len(menu_specs)-2, ("debug", "_Debug"))
|
||||
|
||||
# New classes
|
||||
from IdleHistory import History
|
||||
|
||||
def __init__(self, flist=None):
|
||||
self.interp = ModifiedInterpreter(self)
|
||||
if flist is None:
|
||||
root = Tk()
|
||||
fixwordbreaks(root)
|
||||
root.withdraw()
|
||||
flist = PyShellFileList(root)
|
||||
|
||||
OutputWindow.__init__(self, flist, None, None)
|
||||
|
||||
import __builtin__
|
||||
__builtin__.quit = __builtin__.exit = "To exit, type Ctrl-D."
|
||||
|
||||
self.auto = self.extensions["AutoIndent"] # Required extension
|
||||
self.auto.config(usetabs=1, indentwidth=8, context_use_ps1=1)
|
||||
|
||||
text = self.text
|
||||
text.configure(wrap="char")
|
||||
text.bind("<<newline-and-indent>>", self.enter_callback)
|
||||
text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
|
||||
text.bind("<<interrupt-execution>>", self.cancel_callback)
|
||||
text.bind("<<beginning-of-line>>", self.home_callback)
|
||||
text.bind("<<end-of-file>>", self.eof_callback)
|
||||
text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
|
||||
text.bind("<<toggle-debugger>>", self.toggle_debugger)
|
||||
text.bind("<<open-python-shell>>", self.flist.open_shell)
|
||||
text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
|
||||
|
||||
self.save_stdout = sys.stdout
|
||||
self.save_stderr = sys.stderr
|
||||
self.save_stdin = sys.stdin
|
||||
sys.stdout = PseudoFile(self, "stdout")
|
||||
sys.stderr = PseudoFile(self, "stderr")
|
||||
sys.stdin = self
|
||||
self.console = PseudoFile(self, "console")
|
||||
|
||||
self.history = self.History(self.text)
|
||||
|
||||
reading = 0
|
||||
executing = 0
|
||||
canceled = 0
|
||||
endoffile = 0
|
||||
|
||||
def toggle_debugger(self, event=None):
|
||||
if self.executing:
|
||||
tkMessageBox.showerror("Don't debug now",
|
||||
"You can only toggle the debugger when idle",
|
||||
master=self.text)
|
||||
self.set_debugger_indicator()
|
||||
return "break"
|
||||
else:
|
||||
db = self.interp.getdebugger()
|
||||
if db:
|
||||
self.close_debugger()
|
||||
else:
|
||||
self.open_debugger()
|
||||
|
||||
def set_debugger_indicator(self):
|
||||
db = self.interp.getdebugger()
|
||||
self.setvar("<<toggle-debugger>>", not not db)
|
||||
|
||||
def toggle_jit_stack_viewer( self, event=None):
|
||||
pass # All we need is the variable
|
||||
|
||||
def close_debugger(self):
|
||||
db = self.interp.getdebugger()
|
||||
if db:
|
||||
self.interp.setdebugger(None)
|
||||
db.close()
|
||||
self.resetoutput()
|
||||
self.console.write("[DEBUG OFF]\n")
|
||||
sys.ps1 = ">>> "
|
||||
self.showprompt()
|
||||
self.set_debugger_indicator()
|
||||
|
||||
def open_debugger(self):
|
||||
import Debugger
|
||||
self.interp.setdebugger(Debugger.Debugger(self))
|
||||
sys.ps1 = "[DEBUG ON]\n>>> "
|
||||
self.showprompt()
|
||||
self.set_debugger_indicator()
|
||||
|
||||
def beginexecuting(self):
|
||||
# Helper for ModifiedInterpreter
|
||||
self.resetoutput()
|
||||
self.executing = 1
|
||||
##self._cancel_check = self.cancel_check
|
||||
##sys.settrace(self._cancel_check)
|
||||
|
||||
def endexecuting(self):
|
||||
# Helper for ModifiedInterpreter
|
||||
##sys.settrace(None)
|
||||
##self._cancel_check = None
|
||||
self.executing = 0
|
||||
self.canceled = 0
|
||||
|
||||
def close(self):
|
||||
# Extend base class method
|
||||
if self.executing:
|
||||
# XXX Need to ask a question here
|
||||
if not tkMessageBox.askokcancel(
|
||||
"Kill?",
|
||||
"The program is still running; do you want to kill it?",
|
||||
default="ok",
|
||||
master=self.text):
|
||||
return "cancel"
|
||||
self.canceled = 1
|
||||
if self.reading:
|
||||
self.top.quit()
|
||||
return "cancel"
|
||||
return OutputWindow.close(self)
|
||||
|
||||
def _close(self):
|
||||
self.close_debugger()
|
||||
# Restore std streams
|
||||
sys.stdout = self.save_stdout
|
||||
sys.stderr = self.save_stderr
|
||||
sys.stdin = self.save_stdin
|
||||
# Break cycles
|
||||
self.interp = None
|
||||
self.console = None
|
||||
self.auto = None
|
||||
self.flist.pyshell = None
|
||||
self.history = None
|
||||
OutputWindow._close(self) # Really EditorWindow._close
|
||||
|
||||
def ispythonsource(self, filename):
|
||||
# Override this so EditorWindow never removes the colorizer
|
||||
return True
|
||||
|
||||
def short_title(self):
|
||||
return self.shell_title
|
||||
|
||||
COPYRIGHT = \
|
||||
'Type "copyright", "credits" or "license" for more information.'
|
||||
|
||||
def begin(self):
|
||||
self.resetoutput()
|
||||
self.write("Python %s on %s\n%s\nIDLE %s -- press F1 for help\n" %
|
||||
(sys.version, sys.platform, self.COPYRIGHT,
|
||||
idlever.IDLE_VERSION))
|
||||
try:
|
||||
sys.ps1
|
||||
except AttributeError:
|
||||
sys.ps1 = ">>> "
|
||||
self.showprompt()
|
||||
import Tkinter
|
||||
Tkinter._default_root = None
|
||||
|
||||
def interact(self):
|
||||
self.begin()
|
||||
self.top.mainloop()
|
||||
|
||||
def readline(self):
|
||||
save = self.reading
|
||||
try:
|
||||
self.reading = 1
|
||||
self.top.mainloop()
|
||||
finally:
|
||||
self.reading = save
|
||||
line = self.text.get("iomark", "end-1c")
|
||||
self.resetoutput()
|
||||
if self.canceled:
|
||||
self.canceled = 0
|
||||
raise KeyboardInterrupt
|
||||
if self.endoffile:
|
||||
self.endoffile = 0
|
||||
return ""
|
||||
return line
|
||||
|
||||
def isatty(self):
|
||||
return True
|
||||
|
||||
def cancel_callback(self, event):
|
||||
try:
|
||||
if self.text.compare("sel.first", "!=", "sel.last"):
|
||||
return # Active selection -- always use default binding
|
||||
except:
|
||||
pass
|
||||
if not (self.executing or self.reading):
|
||||
self.resetoutput()
|
||||
self.write("KeyboardInterrupt\n")
|
||||
self.showprompt()
|
||||
return "break"
|
||||
self.endoffile = 0
|
||||
self.canceled = 1
|
||||
if self.reading:
|
||||
self.top.quit()
|
||||
return "break"
|
||||
|
||||
def eof_callback(self, event):
|
||||
if self.executing and not self.reading:
|
||||
return # Let the default binding (delete next char) take over
|
||||
if not (self.text.compare("iomark", "==", "insert") and
|
||||
self.text.compare("insert", "==", "end-1c")):
|
||||
return # Let the default binding (delete next char) take over
|
||||
if not self.executing:
|
||||
## if not tkMessageBox.askokcancel(
|
||||
## "Exit?",
|
||||
## "Are you sure you want to exit?",
|
||||
## default="ok", master=self.text):
|
||||
## return "break"
|
||||
self.resetoutput()
|
||||
self.close()
|
||||
else:
|
||||
self.canceled = 0
|
||||
self.endoffile = 1
|
||||
self.top.quit()
|
||||
return "break"
|
||||
|
||||
def home_callback(self, event):
|
||||
if event.state != 0 and event.keysym == "Home":
|
||||
return # <Modifier-Home>; fall back to class binding
|
||||
if self.text.compare("iomark", "<=", "insert") and \
|
||||
self.text.compare("insert linestart", "<=", "iomark"):
|
||||
self.text.mark_set("insert", "iomark")
|
||||
self.text.tag_remove("sel", "1.0", "end")
|
||||
self.text.see("insert")
|
||||
return "break"
|
||||
|
||||
def linefeed_callback(self, event):
|
||||
# Insert a linefeed without entering anything (still autoindented)
|
||||
if self.reading:
|
||||
self.text.insert("insert", "\n")
|
||||
self.text.see("insert")
|
||||
else:
|
||||
self.auto.auto_indent(event)
|
||||
return "break"
|
||||
|
||||
def enter_callback(self, event):
|
||||
if self.executing and not self.reading:
|
||||
return # Let the default binding (insert '\n') take over
|
||||
# If some text is selected, recall the selection
|
||||
# (but only if this before the I/O mark)
|
||||
try:
|
||||
sel = self.text.get("sel.first", "sel.last")
|
||||
if sel:
|
||||
if self.text.compare("sel.last", "<=", "iomark"):
|
||||
self.recall(sel)
|
||||
return "break"
|
||||
except:
|
||||
pass
|
||||
# If we're strictly before the line containing iomark, recall
|
||||
# the current line, less a leading prompt, less leading or
|
||||
# trailing whitespace
|
||||
if self.text.compare("insert", "<", "iomark linestart"):
|
||||
# Check if there's a relevant stdin range -- if so, use it
|
||||
prev = self.text.tag_prevrange("stdin", "insert")
|
||||
if prev and self.text.compare("insert", "<", prev[1]):
|
||||
self.recall(self.text.get(prev[0], prev[1]))
|
||||
return "break"
|
||||
next = self.text.tag_nextrange("stdin", "insert")
|
||||
if next and self.text.compare("insert lineend", ">=", next[0]):
|
||||
self.recall(self.text.get(next[0], next[1]))
|
||||
return "break"
|
||||
# No stdin mark -- just get the current line
|
||||
self.recall(self.text.get("insert linestart", "insert lineend"))
|
||||
return "break"
|
||||
# If we're in the current input and there's only whitespace
|
||||
# beyond the cursor, erase that whitespace first
|
||||
s = self.text.get("insert", "end-1c")
|
||||
if s and not s.strip():
|
||||
self.text.delete("insert", "end-1c")
|
||||
# If we're in the current input before its last line,
|
||||
# insert a newline right at the insert point
|
||||
if self.text.compare("insert", "<", "end-1c linestart"):
|
||||
self.auto.auto_indent(event)
|
||||
return "break"
|
||||
# We're in the last line; append a newline and submit it
|
||||
self.text.mark_set("insert", "end-1c")
|
||||
if self.reading:
|
||||
self.text.insert("insert", "\n")
|
||||
self.text.see("insert")
|
||||
else:
|
||||
self.auto.auto_indent(event)
|
||||
self.text.tag_add("stdin", "iomark", "end-1c")
|
||||
self.text.update_idletasks()
|
||||
if self.reading:
|
||||
self.top.quit() # Break out of recursive mainloop() in raw_input()
|
||||
else:
|
||||
self.runit()
|
||||
return "break"
|
||||
|
||||
def recall(self, s):
|
||||
if self.history:
|
||||
self.history.recall(s)
|
||||
|
||||
def runit(self):
|
||||
line = self.text.get("iomark", "end-1c")
|
||||
# Strip off last newline and surrounding whitespace.
|
||||
# (To allow you to hit return twice to end a statement.)
|
||||
i = len(line)
|
||||
while i > 0 and line[i-1] in " \t":
|
||||
i = i-1
|
||||
if i > 0 and line[i-1] == "\n":
|
||||
i = i-1
|
||||
while i > 0 and line[i-1] in " \t":
|
||||
i = i-1
|
||||
line = line[:i]
|
||||
more = self.interp.runsource(line)
|
||||
if not more:
|
||||
self.showprompt()
|
||||
|
||||
def cancel_check(self, frame, what, args,
|
||||
dooneevent=tkinter.dooneevent,
|
||||
dontwait=tkinter.DONT_WAIT):
|
||||
# Hack -- use the debugger hooks to be able to handle events
|
||||
# and interrupt execution at any time.
|
||||
# This slows execution down quite a bit, so you may want to
|
||||
# disable this (by not calling settrace() in runcode() above)
|
||||
# for full-bore (uninterruptable) speed.
|
||||
# XXX This should become a user option.
|
||||
if self.canceled:
|
||||
return
|
||||
dooneevent(dontwait)
|
||||
if self.canceled:
|
||||
self.canceled = 0
|
||||
raise KeyboardInterrupt
|
||||
return self._cancel_check
|
||||
|
||||
def open_stack_viewer(self, event=None):
|
||||
try:
|
||||
sys.last_traceback
|
||||
except:
|
||||
tkMessageBox.showerror("No stack trace",
|
||||
"There is no stack trace yet.\n"
|
||||
"(sys.last_traceback is not defined)",
|
||||
master=self.text)
|
||||
return
|
||||
from StackViewer import StackBrowser
|
||||
sv = StackBrowser(self.root, self.flist)
|
||||
|
||||
def showprompt(self):
|
||||
self.resetoutput()
|
||||
try:
|
||||
s = str(sys.ps1)
|
||||
except:
|
||||
s = ""
|
||||
self.console.write(s)
|
||||
self.text.mark_set("insert", "end-1c")
|
||||
|
||||
def resetoutput(self):
|
||||
source = self.text.get("iomark", "end-1c")
|
||||
if self.history:
|
||||
self.history.history_store(source)
|
||||
if self.text.get("end-2c") != "\n":
|
||||
self.text.insert("end-1c", "\n")
|
||||
self.text.mark_set("iomark", "end-1c")
|
||||
sys.stdout.softspace = 0
|
||||
|
||||
def write(self, s, tags=()):
|
||||
self.text.mark_gravity("iomark", "right")
|
||||
OutputWindow.write(self, s, tags, "iomark")
|
||||
self.text.mark_gravity("iomark", "left")
|
||||
if self.canceled:
|
||||
self.canceled = 0
|
||||
raise KeyboardInterrupt
|
||||
|
||||
class PseudoFile:
|
||||
|
||||
def __init__(self, shell, tags):
|
||||
self.shell = shell
|
||||
self.tags = tags
|
||||
|
||||
def write(self, s):
|
||||
self.shell.write(s, self.tags)
|
||||
|
||||
def writelines(self, l):
|
||||
map(self.write, l)
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
def isatty(self):
|
||||
return True
|
||||
|
||||
|
||||
usage_msg = """\
|
||||
usage: idle.py [-c command] [-d] [-e] [-s] [-t title] [arg] ...
|
||||
|
||||
-c command run this command
|
||||
-d enable debugger
|
||||
-e edit mode; arguments are files to be edited
|
||||
-s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
|
||||
-t title set title of shell window
|
||||
|
||||
When neither -c nor -e is used, and there are arguments, and the first
|
||||
argument is not '-', the first argument is run as a script. Remaining
|
||||
arguments are arguments to the script or to the command run by -c.
|
||||
"""
|
||||
|
||||
def main():
|
||||
cmd = None
|
||||
edit = 0
|
||||
debug = 0
|
||||
startup = 0
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "c:deist:")
|
||||
except getopt.error, msg:
|
||||
sys.stderr.write("Error: %s\n" % str(msg))
|
||||
sys.stderr.write(usage_msg)
|
||||
sys.exit(2)
|
||||
|
||||
for o, a in opts:
|
||||
if o == '-c':
|
||||
cmd = a
|
||||
if o == '-d':
|
||||
debug = 1
|
||||
if o == '-e':
|
||||
edit = 1
|
||||
if o == '-s':
|
||||
startup = 1
|
||||
if o == '-t':
|
||||
PyShell.shell_title = a
|
||||
|
||||
for i in range(len(sys.path)):
|
||||
sys.path[i] = os.path.abspath(sys.path[i])
|
||||
|
||||
pathx = []
|
||||
if edit:
|
||||
for filename in args:
|
||||
pathx.append(os.path.dirname(filename))
|
||||
elif args and args[0] != "-":
|
||||
pathx.append(os.path.dirname(args[0]))
|
||||
else:
|
||||
pathx.append(os.curdir)
|
||||
for dir in pathx:
|
||||
dir = os.path.abspath(dir)
|
||||
if not dir in sys.path:
|
||||
sys.path.insert(0, dir)
|
||||
|
||||
global flist, root
|
||||
root = Tk(className="Idle")
|
||||
fixwordbreaks(root)
|
||||
root.withdraw()
|
||||
flist = PyShellFileList(root)
|
||||
|
||||
if edit:
|
||||
for filename in args:
|
||||
flist.open(filename)
|
||||
else:
|
||||
if cmd:
|
||||
sys.argv = ["-c"] + args
|
||||
else:
|
||||
sys.argv = args or [""]
|
||||
|
||||
|
||||
shell = PyShell(flist)
|
||||
interp = shell.interp
|
||||
flist.pyshell = shell
|
||||
|
||||
if startup:
|
||||
filename = os.environ.get("IDLESTARTUP") or \
|
||||
os.environ.get("PYTHONSTARTUP")
|
||||
if filename and os.path.isfile(filename):
|
||||
interp.execfile(filename)
|
||||
|
||||
if debug:
|
||||
shell.open_debugger()
|
||||
if cmd:
|
||||
interp.execsource(cmd)
|
||||
elif not edit and args and args[0] != "-":
|
||||
interp.execfile(args[0])
|
||||
|
||||
shell.begin()
|
||||
root.mainloop()
|
||||
root.destroy()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,53 +0,0 @@
|
|||
IDLE 0.5 - February 2000
|
||||
------------------------
|
||||
|
||||
This is an early release of IDLE, my own attempt at a Tkinter-based
|
||||
IDE for Python.
|
||||
|
||||
For news about this release, see the file NEWS.txt. (For a more
|
||||
detailed change log, see the file ChangeLog.)
|
||||
|
||||
FEATURES
|
||||
|
||||
IDLE has the following features:
|
||||
|
||||
- coded in 100% pure Python, using the Tkinter GUI toolkit (i.e. Tcl/Tk)
|
||||
|
||||
- cross-platform: works on Windows and Unix (on the Mac, there are
|
||||
currently problems with Tcl/Tk)
|
||||
|
||||
- multi-window text editor with multiple undo, Python colorizing
|
||||
and many other features, e.g. smart indent and call tips
|
||||
|
||||
- Python shell window (a.k.a. interactive interpreter)
|
||||
|
||||
- debugger (not complete, but you can set breakpoints, view and step)
|
||||
|
||||
USAGE
|
||||
|
||||
The main program is in the file "idle.py"; on Unix, you should be able
|
||||
to run it by typing "./idle.py" to your shell. On Windows, you can
|
||||
run it by double-clicking it; you can use idle.pyw to avoid popping up
|
||||
a DOS console. If you want to pass command line arguments on Windows,
|
||||
use the batch file idle.bat.
|
||||
|
||||
Command line arguments: files passed on the command line are executed,
|
||||
not opened for editing, unless you give the -e command line option.
|
||||
Try "./idle.py -h" to see other command line options.
|
||||
|
||||
IDLE requires Python 1.5.2 or later, so it is currently only usable
|
||||
with a Python 1.5.2 (or later) distribution. (An older version of
|
||||
IDLE is distributed with Python 1.5.2; you can drop this version on
|
||||
top of it.)
|
||||
|
||||
COPYRIGHT
|
||||
|
||||
IDLE is covered by the standard Python copyright notice
|
||||
(http://www.python.org/doc/Copyright.html).
|
||||
|
||||
FEEDBACK
|
||||
|
||||
For feedback, please use the Python Bugs List
|
||||
(http://www.python.org/search/search_bugs.html).
|
||||
|
||||
--Guido van Rossum (home page: http://www.python.org/~guido/)
|
|
@ -1,341 +0,0 @@
|
|||
import select
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
import types
|
||||
|
||||
VERBOSE = None
|
||||
|
||||
class SocketProtocol:
|
||||
"""A simple protocol for sending strings across a socket"""
|
||||
BUF_SIZE = 8192
|
||||
|
||||
def __init__(self, sock):
|
||||
self.sock = sock
|
||||
self._buffer = ''
|
||||
self._closed = 0
|
||||
|
||||
def close(self):
|
||||
self._closed = 1
|
||||
self.sock.close()
|
||||
|
||||
def send(self, buf):
|
||||
"""Encode buf and write it on the socket"""
|
||||
if VERBOSE:
|
||||
VERBOSE.write('send %d:%s\n' % (len(buf), `buf`))
|
||||
self.sock.send('%d:%s' % (len(buf), buf))
|
||||
|
||||
def receive(self, timeout=0):
|
||||
"""Get next complete string from socket or return None
|
||||
|
||||
Raise EOFError on EOF
|
||||
"""
|
||||
buf = self._read_from_buffer()
|
||||
if buf is not None:
|
||||
return buf
|
||||
recvbuf = self._read_from_socket(timeout)
|
||||
if recvbuf is None:
|
||||
return None
|
||||
if recvbuf == '' and self._buffer == '':
|
||||
raise EOFError
|
||||
if VERBOSE:
|
||||
VERBOSE.write('recv %s\n' % `recvbuf`)
|
||||
self._buffer = self._buffer + recvbuf
|
||||
r = self._read_from_buffer()
|
||||
return r
|
||||
|
||||
def _read_from_socket(self, timeout):
|
||||
"""Does not block"""
|
||||
if self._closed:
|
||||
return ''
|
||||
if timeout is not None:
|
||||
r, w, x = select.select([self.sock], [], [], timeout)
|
||||
if timeout is None or r:
|
||||
return self.sock.recv(self.BUF_SIZE)
|
||||
else:
|
||||
return None
|
||||
|
||||
def _read_from_buffer(self):
|
||||
buf = self._buffer
|
||||
i = buf.find(':')
|
||||
if i == -1:
|
||||
return None
|
||||
buflen = int(buf[:i])
|
||||
enclen = i + 1 + buflen
|
||||
if len(buf) >= enclen:
|
||||
s = buf[i+1:enclen]
|
||||
self._buffer = buf[enclen:]
|
||||
return s
|
||||
else:
|
||||
self._buffer = buf
|
||||
return None
|
||||
|
||||
# helpers for registerHandler method below
|
||||
|
||||
def get_methods(obj):
|
||||
methods = []
|
||||
for name in dir(obj):
|
||||
attr = getattr(obj, name)
|
||||
if callable(attr):
|
||||
methods.append(name)
|
||||
if type(obj) == types.InstanceType:
|
||||
methods = methods + get_methods(obj.__class__)
|
||||
if type(obj) == types.ClassType:
|
||||
for super in obj.__bases__:
|
||||
methods = methods + get_methods(super)
|
||||
return methods
|
||||
|
||||
class CommandProtocol:
|
||||
def __init__(self, sockp):
|
||||
self.sockp = sockp
|
||||
self.seqno = 0
|
||||
self.handlers = {}
|
||||
|
||||
def close(self):
|
||||
self.sockp.close()
|
||||
self.handlers.clear()
|
||||
|
||||
def registerHandler(self, handler):
|
||||
"""A Handler is an object with handle_XXX methods"""
|
||||
for methname in get_methods(handler):
|
||||
if methname[:7] == "handle_":
|
||||
name = methname[7:]
|
||||
self.handlers[name] = getattr(handler, methname)
|
||||
|
||||
def send(self, cmd, arg='', seqno=None):
|
||||
if arg:
|
||||
msg = "%s %s" % (cmd, arg)
|
||||
else:
|
||||
msg = cmd
|
||||
if seqno is None:
|
||||
seqno = self.get_seqno()
|
||||
msgbuf = self.encode_seqno(seqno) + msg
|
||||
self.sockp.send(msgbuf)
|
||||
if cmd == "reply":
|
||||
return
|
||||
reply = self.sockp.receive(timeout=None)
|
||||
r_cmd, r_arg, r_seqno = self._decode_msg(reply)
|
||||
assert r_seqno == seqno and r_cmd == "reply", "bad reply"
|
||||
return r_arg
|
||||
|
||||
def _decode_msg(self, msg):
|
||||
seqno = self.decode_seqno(msg[:self.SEQNO_ENC_LEN])
|
||||
msg = msg[self.SEQNO_ENC_LEN:]
|
||||
parts = msg.split(" ", 2)
|
||||
if len(parts) == 1:
|
||||
cmd = msg
|
||||
arg = ''
|
||||
else:
|
||||
cmd = parts[0]
|
||||
arg = parts[1]
|
||||
return cmd, arg, seqno
|
||||
|
||||
def dispatch(self):
|
||||
msg = self.sockp.receive()
|
||||
if msg is None:
|
||||
return
|
||||
cmd, arg, seqno = self._decode_msg(msg)
|
||||
self._current_reply = seqno
|
||||
h = self.handlers.get(cmd, self.default_handler)
|
||||
try:
|
||||
r = h(arg)
|
||||
except TypeError, msg:
|
||||
raise TypeError, "handle_%s: %s" % (cmd, msg)
|
||||
if self._current_reply is None:
|
||||
if r is not None:
|
||||
sys.stderr.write("ignoring %s return value type %s\n" % \
|
||||
(cmd, type(r).__name__))
|
||||
return
|
||||
if r is None:
|
||||
r = ''
|
||||
if type(r) != types.StringType:
|
||||
raise ValueError, "invalid return type for %s" % cmd
|
||||
self.send("reply", r, seqno=seqno)
|
||||
|
||||
def reply(self, arg=''):
|
||||
"""Send a reply immediately
|
||||
|
||||
otherwise reply will be sent when handler returns
|
||||
"""
|
||||
self.send("reply", arg, self._current_reply)
|
||||
self._current_reply = None
|
||||
|
||||
def default_handler(self, arg):
|
||||
sys.stderr.write("WARNING: unhandled message %s\n" % arg)
|
||||
return ''
|
||||
|
||||
SEQNO_ENC_LEN = 4
|
||||
|
||||
def get_seqno(self):
|
||||
seqno = self.seqno
|
||||
self.seqno = seqno + 1
|
||||
return seqno
|
||||
|
||||
def encode_seqno(self, seqno):
|
||||
return struct.pack("I", seqno)
|
||||
|
||||
def decode_seqno(self, buf):
|
||||
return struct.unpack("I", buf)[0]
|
||||
|
||||
|
||||
class StdioRedirector:
|
||||
"""Redirect sys.std{in,out,err} to a set of file-like objects"""
|
||||
|
||||
def __init__(self, stdin, stdout, stderr):
|
||||
self.stdin = stdin
|
||||
self.stdout = stdout
|
||||
self.stderr = stderr
|
||||
|
||||
def redirect(self):
|
||||
self.save()
|
||||
sys.stdin = self.stdin
|
||||
sys.stdout = self.stdout
|
||||
sys.stderr = self.stderr
|
||||
|
||||
def save(self):
|
||||
self._stdin = sys.stdin
|
||||
self._stdout = sys.stdout
|
||||
self._stderr = sys.stderr
|
||||
|
||||
def restore(self):
|
||||
sys.stdin = self._stdin
|
||||
sys.stdout = self._stdout
|
||||
sys.stderr = self._stderr
|
||||
|
||||
class IOWrapper:
|
||||
"""Send output from a file-like object across a SocketProtocol
|
||||
|
||||
XXX Should this be more tightly integrated with the CommandProtocol?
|
||||
"""
|
||||
|
||||
def __init__(self, name, cmdp):
|
||||
self.name = name
|
||||
self.cmdp = cmdp
|
||||
self.buffer = []
|
||||
|
||||
class InputWrapper(IOWrapper):
|
||||
def write(self, buf):
|
||||
# XXX what should this do on Windows?
|
||||
raise IOError, (9, '[Errno 9] Bad file descriptor')
|
||||
|
||||
def read(self, arg=None):
|
||||
if arg is not None:
|
||||
if arg <= 0:
|
||||
return ''
|
||||
else:
|
||||
arg = 0
|
||||
return self.cmdp.send(self.name, "read,%s" % arg)
|
||||
|
||||
def readline(self):
|
||||
return self.cmdp.send(self.name, "readline")
|
||||
|
||||
class OutputWrapper(IOWrapper):
|
||||
def write(self, buf):
|
||||
self.cmdp.send(self.name, buf)
|
||||
|
||||
def read(self, arg=None):
|
||||
return ''
|
||||
|
||||
class RemoteInterp:
|
||||
def __init__(self, sock):
|
||||
self._sock = SocketProtocol(sock)
|
||||
self._cmd = CommandProtocol(self._sock)
|
||||
self._cmd.registerHandler(self)
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
while 1:
|
||||
self._cmd.dispatch()
|
||||
except EOFError:
|
||||
pass
|
||||
|
||||
def handle_execfile(self, arg):
|
||||
self._cmd.reply()
|
||||
io = StdioRedirector(InputWrapper("stdin", self._cmd),
|
||||
OutputWrapper("stdout", self._cmd),
|
||||
OutputWrapper("stderr", self._cmd))
|
||||
io.redirect()
|
||||
execfile(arg, {'__name__':'__main__'})
|
||||
io.restore()
|
||||
self._cmd.send("terminated")
|
||||
|
||||
def handle_quit(self, arg):
|
||||
self._cmd.reply()
|
||||
self._cmd.close()
|
||||
|
||||
def startRemoteInterp(id):
|
||||
import os
|
||||
# UNIX domain sockets are simpler for starters
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
sock.bind("/var/tmp/ri.%s" % id)
|
||||
try:
|
||||
sock.listen(1)
|
||||
cli, addr = sock.accept()
|
||||
rinterp = RemoteInterp(cli)
|
||||
rinterp.run()
|
||||
finally:
|
||||
os.unlink("/var/tmp/ri.%s" % id)
|
||||
|
||||
class RIClient:
|
||||
"""Client of the remote interpreter"""
|
||||
def __init__(self, sock):
|
||||
self._sock = SocketProtocol(sock)
|
||||
self._cmd = CommandProtocol(self._sock)
|
||||
self._cmd.registerHandler(self)
|
||||
|
||||
def execfile(self, file):
|
||||
self._cmd.send("execfile", file)
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
while 1:
|
||||
self._cmd.dispatch()
|
||||
except EOFError:
|
||||
pass
|
||||
|
||||
def handle_stdout(self, buf):
|
||||
sys.stdout.write(buf)
|
||||
## sys.stdout.flush()
|
||||
|
||||
def handle_stderr(self, buf):
|
||||
sys.stderr.write(buf)
|
||||
|
||||
def handle_stdin(self, arg):
|
||||
if arg == "readline":
|
||||
return sys.stdin.readline()
|
||||
i = arg.find(",") + 1
|
||||
bytes = int(arg[i:])
|
||||
if bytes == 0:
|
||||
return sys.stdin.read()
|
||||
else:
|
||||
return sys.stdin.read(bytes)
|
||||
|
||||
def handle_terminated(self, arg):
|
||||
self._cmd.reply()
|
||||
self._cmd.send("quit")
|
||||
self._cmd.close()
|
||||
|
||||
def riExec(id, file):
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
sock.connect("/var/tmp/ri.%s" % id)
|
||||
cli = RIClient(sock)
|
||||
cli.execfile(file)
|
||||
cli.run()
|
||||
|
||||
if __name__ == "__main__":
|
||||
import getopt
|
||||
|
||||
SERVER = 1
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'cv')
|
||||
for o, v in opts:
|
||||
if o == '-c':
|
||||
SERVER = 0
|
||||
elif o == '-v':
|
||||
VERBOSE = sys.stderr
|
||||
id = args[0]
|
||||
|
||||
if SERVER:
|
||||
startRemoteInterp(id)
|
||||
else:
|
||||
file = args[1]
|
||||
riExec(id, file)
|
|
@ -1,167 +0,0 @@
|
|||
from Tkinter import *
|
||||
import SearchEngine
|
||||
from SearchDialogBase import SearchDialogBase
|
||||
|
||||
def replace(text):
|
||||
root = text._root()
|
||||
engine = SearchEngine.get(root)
|
||||
if not hasattr(engine, "_replacedialog"):
|
||||
engine._replacedialog = ReplaceDialog(root, engine)
|
||||
dialog = engine._replacedialog
|
||||
dialog.open(text)
|
||||
|
||||
class ReplaceDialog(SearchDialogBase):
|
||||
|
||||
title = "Replace Dialog"
|
||||
icon = "Replace"
|
||||
|
||||
def __init__(self, root, engine):
|
||||
SearchDialogBase.__init__(self, root, engine)
|
||||
self.replvar = StringVar(root)
|
||||
|
||||
def open(self, text):
|
||||
SearchDialogBase.open(self, text)
|
||||
try:
|
||||
first = text.index("sel.first")
|
||||
except TclError:
|
||||
first = None
|
||||
try:
|
||||
last = text.index("sel.last")
|
||||
except TclError:
|
||||
last = None
|
||||
first = first or text.index("insert")
|
||||
last = last or first
|
||||
self.show_hit(first, last)
|
||||
self.ok = 1
|
||||
|
||||
def create_entries(self):
|
||||
SearchDialogBase.create_entries(self)
|
||||
self.replent = self.make_entry("Replace with:", self.replvar)
|
||||
|
||||
def create_command_buttons(self):
|
||||
SearchDialogBase.create_command_buttons(self)
|
||||
self.make_button("Find", self.find_it)
|
||||
self.make_button("Replace", self.replace_it)
|
||||
self.make_button("Replace+Find", self.default_command, 1)
|
||||
self.make_button("Replace All", self.replace_all)
|
||||
|
||||
def find_it(self, event=None):
|
||||
self.do_find(0)
|
||||
|
||||
def replace_it(self, event=None):
|
||||
if self.do_find(self.ok):
|
||||
self.do_replace()
|
||||
|
||||
def default_command(self, event=None):
|
||||
if self.do_find(self.ok):
|
||||
self.do_replace()
|
||||
self.do_find(0)
|
||||
|
||||
def replace_all(self, event=None):
|
||||
prog = self.engine.getprog()
|
||||
if not prog:
|
||||
return
|
||||
repl = self.replvar.get()
|
||||
text = self.text
|
||||
res = self.engine.search_text(text, prog)
|
||||
if not res:
|
||||
text.bell()
|
||||
return
|
||||
text.tag_remove("sel", "1.0", "end")
|
||||
text.tag_remove("hit", "1.0", "end")
|
||||
line = res[0]
|
||||
col = res[1].start()
|
||||
if self.engine.iswrap():
|
||||
line = 1
|
||||
col = 0
|
||||
ok = 1
|
||||
first = last = None
|
||||
# XXX ought to replace circular instead of top-to-bottom when wrapping
|
||||
text.undo_block_start()
|
||||
while 1:
|
||||
res = self.engine.search_forward(text, prog, line, col, 0, ok)
|
||||
if not res:
|
||||
break
|
||||
line, m = res
|
||||
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||
orig = m.group()
|
||||
new = m.expand(repl)
|
||||
i, j = m.span()
|
||||
first = "%d.%d" % (line, i)
|
||||
last = "%d.%d" % (line, j)
|
||||
if new == orig:
|
||||
text.mark_set("insert", last)
|
||||
else:
|
||||
text.mark_set("insert", first)
|
||||
if first != last:
|
||||
text.delete(first, last)
|
||||
if new:
|
||||
text.insert(first, new)
|
||||
col = i + len(new)
|
||||
ok = 0
|
||||
text.undo_block_stop()
|
||||
if first and last:
|
||||
self.show_hit(first, last)
|
||||
self.close()
|
||||
|
||||
def do_find(self, ok=0):
|
||||
if not self.engine.getprog():
|
||||
return False
|
||||
text = self.text
|
||||
res = self.engine.search_text(text, None, ok)
|
||||
if not res:
|
||||
text.bell()
|
||||
return False
|
||||
line, m = res
|
||||
i, j = m.span()
|
||||
first = "%d.%d" % (line, i)
|
||||
last = "%d.%d" % (line, j)
|
||||
self.show_hit(first, last)
|
||||
self.ok = 1
|
||||
return True
|
||||
|
||||
def do_replace(self):
|
||||
prog = self.engine.getprog()
|
||||
if not prog:
|
||||
return False
|
||||
text = self.text
|
||||
try:
|
||||
first = pos = text.index("sel.first")
|
||||
last = text.index("sel.last")
|
||||
except TclError:
|
||||
pos = None
|
||||
if not pos:
|
||||
first = last = pos = text.index("insert")
|
||||
line, col = SearchEngine.get_line_col(pos)
|
||||
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||
m = prog.match(chars, col)
|
||||
if not prog:
|
||||
return False
|
||||
new = m.expand(self.replvar.get())
|
||||
text.mark_set("insert", first)
|
||||
text.undo_block_start()
|
||||
if m.group():
|
||||
text.delete(first, last)
|
||||
if new:
|
||||
text.insert(first, new)
|
||||
text.undo_block_stop()
|
||||
self.show_hit(first, text.index("insert"))
|
||||
self.ok = 0
|
||||
return True
|
||||
|
||||
def show_hit(self, first, last):
|
||||
text = self.text
|
||||
text.mark_set("insert", first)
|
||||
text.tag_remove("sel", "1.0", "end")
|
||||
text.tag_add("sel", first, last)
|
||||
text.tag_remove("hit", "1.0", "end")
|
||||
if first == last:
|
||||
text.tag_add("hit", first)
|
||||
else:
|
||||
text.tag_add("hit", first, last)
|
||||
text.see("insert")
|
||||
text.update_idletasks()
|
||||
|
||||
def close(self, event=None):
|
||||
SearchDialogBase.close(self, event)
|
||||
self.text.tag_remove("hit", "1.0", "end")
|
|
@ -1,175 +0,0 @@
|
|||
"""Extension to execute code outside the Python shell window.
|
||||
|
||||
This adds the following commands (to the Edit menu, until there's a
|
||||
separate Python menu):
|
||||
|
||||
- Check module (Alt-F5) does a full syntax check of the current module.
|
||||
It also runs the tabnanny to catch any inconsistent tabs.
|
||||
|
||||
- Import module (F5) is equivalent to either import or reload of the
|
||||
current module. The window must have been saved previously. The
|
||||
module is added to sys.modules, and is also added to the __main__
|
||||
namespace. Output goes to the shell window.
|
||||
|
||||
- Run module (Control-F5) does the same but executes the module's
|
||||
code in the __main__ namespace.
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import imp
|
||||
import tkMessageBox
|
||||
|
||||
indent_message = """Error: Inconsistent indentation detected!
|
||||
|
||||
This means that either:
|
||||
|
||||
(1) your indentation is outright incorrect (easy to fix), or
|
||||
|
||||
(2) your indentation mixes tabs and spaces in a way that depends on \
|
||||
how many spaces a tab is worth.
|
||||
|
||||
To fix case 2, change all tabs to spaces by using Select All followed \
|
||||
by Untabify Region (both in the Edit menu)."""
|
||||
|
||||
class ScriptBinding:
|
||||
|
||||
keydefs = {
|
||||
'<<check-module>>': ['<Alt-F5>', '<Meta-F5>'],
|
||||
'<<import-module>>': ['<F5>'],
|
||||
'<<run-script>>': ['<Control-F5>'],
|
||||
}
|
||||
|
||||
menudefs = [
|
||||
('edit', [None,
|
||||
('Check module', '<<check-module>>'),
|
||||
('Import module', '<<import-module>>'),
|
||||
('Run script', '<<run-script>>'),
|
||||
]
|
||||
),
|
||||
]
|
||||
|
||||
def __init__(self, editwin):
|
||||
if not editwin.runnable:
|
||||
self.menudefs = []
|
||||
self.keydefs = {}
|
||||
self.editwin = editwin
|
||||
# Provide instance variables referenced by Debugger
|
||||
# XXX This should be done differently
|
||||
self.flist = self.editwin.flist
|
||||
self.root = self.flist.root
|
||||
|
||||
def check_module_event(self, event):
|
||||
filename = self.getfilename()
|
||||
if not filename:
|
||||
return
|
||||
if not self.tabnanny(filename):
|
||||
return
|
||||
if not self.checksyntax(filename):
|
||||
return
|
||||
|
||||
def tabnanny(self, filename):
|
||||
import tabnanny
|
||||
import tokenize
|
||||
f = open(filename, 'r')
|
||||
try:
|
||||
tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
|
||||
except tokenize.TokenError, msg:
|
||||
self.errorbox("Token error",
|
||||
"Token error:\n%s" % str(msg))
|
||||
return 0
|
||||
except tabnanny.NannyNag, nag:
|
||||
# The error messages from tabnanny are too confusing...
|
||||
self.editwin.gotoline(nag.get_lineno())
|
||||
self.errorbox("Tab/space error", indent_message)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def checksyntax(self, filename):
|
||||
f = open(filename, 'r')
|
||||
source = f.read()
|
||||
f.close()
|
||||
if '\r' in source:
|
||||
import re
|
||||
source = re.sub(r"\r\n", "\n", source)
|
||||
if source and source[-1] != '\n':
|
||||
source = source + '\n'
|
||||
try:
|
||||
compile(source, filename, "exec")
|
||||
except (SyntaxError, OverflowError), err:
|
||||
try:
|
||||
msg, (errorfilename, lineno, offset, line) = err
|
||||
if not errorfilename:
|
||||
err.args = msg, (filename, lineno, offset, line)
|
||||
err.filename = filename
|
||||
except:
|
||||
lineno = None
|
||||
msg = "*** " + str(err)
|
||||
if lineno:
|
||||
self.editwin.gotoline(lineno)
|
||||
self.errorbox("Syntax error",
|
||||
"There's an error in your program:\n" + msg)
|
||||
return 1
|
||||
|
||||
def import_module_event(self, event):
|
||||
filename = self.getfilename()
|
||||
if not filename:
|
||||
return
|
||||
|
||||
modname, ext = os.path.splitext(os.path.basename(filename))
|
||||
if sys.modules.has_key(modname):
|
||||
mod = sys.modules[modname]
|
||||
else:
|
||||
mod = imp.new_module(modname)
|
||||
sys.modules[modname] = mod
|
||||
mod.__file__ = filename
|
||||
setattr(sys.modules['__main__'], modname, mod)
|
||||
|
||||
dir = os.path.dirname(filename)
|
||||
dir = os.path.normpath(os.path.abspath(dir))
|
||||
if dir not in sys.path:
|
||||
sys.path.insert(0, dir)
|
||||
|
||||
flist = self.editwin.flist
|
||||
shell = flist.open_shell()
|
||||
interp = shell.interp
|
||||
interp.runcode("reload(%s)" % modname)
|
||||
|
||||
def run_script_event(self, event):
|
||||
filename = self.getfilename()
|
||||
if not filename:
|
||||
return
|
||||
|
||||
flist = self.editwin.flist
|
||||
shell = flist.open_shell()
|
||||
interp = shell.interp
|
||||
if (not sys.argv or
|
||||
os.path.basename(sys.argv[0]) != os.path.basename(filename)):
|
||||
# XXX Too often this discards arguments the user just set...
|
||||
sys.argv = [filename]
|
||||
interp.execfile(filename)
|
||||
|
||||
def getfilename(self):
|
||||
# Logic to make sure we have a saved filename
|
||||
# XXX Better logic would offer to save!
|
||||
if not self.editwin.get_saved():
|
||||
name = (self.editwin.short_title() or
|
||||
self.editwin.long_title() or
|
||||
"Untitled")
|
||||
self.errorbox("Not saved",
|
||||
"The buffer for %s is not saved.\n" % name +
|
||||
"Please save it first!")
|
||||
self.editwin.text.focus_set()
|
||||
return
|
||||
filename = self.editwin.io.filename
|
||||
if not filename:
|
||||
self.errorbox("No file name",
|
||||
"This window has no file name")
|
||||
return
|
||||
return filename
|
||||
|
||||
def errorbox(self, title, message):
|
||||
# XXX This should really be a function of EditorWindow...
|
||||
tkMessageBox.showerror(title, message, master=self.editwin.text)
|
||||
self.editwin.text.focus_set()
|
|
@ -1,139 +0,0 @@
|
|||
from Tkinter import *
|
||||
|
||||
class ScrolledList:
|
||||
|
||||
default = "(None)"
|
||||
|
||||
def __init__(self, master, **options):
|
||||
# Create top frame, with scrollbar and listbox
|
||||
self.master = master
|
||||
self.frame = frame = Frame(master)
|
||||
self.frame.pack(fill="both", expand=1)
|
||||
self.vbar = vbar = Scrollbar(frame, name="vbar")
|
||||
self.vbar.pack(side="right", fill="y")
|
||||
self.listbox = listbox = Listbox(frame, exportselection=0,
|
||||
background="white")
|
||||
if options:
|
||||
listbox.configure(options)
|
||||
listbox.pack(expand=1, fill="both")
|
||||
# Tie listbox and scrollbar together
|
||||
vbar["command"] = listbox.yview
|
||||
listbox["yscrollcommand"] = vbar.set
|
||||
# Bind events to the list box
|
||||
listbox.bind("<ButtonRelease-1>", self.click_event)
|
||||
listbox.bind("<Double-ButtonRelease-1>", self.double_click_event)
|
||||
listbox.bind("<ButtonPress-3>", self.popup_event)
|
||||
listbox.bind("<Key-Up>", self.up_event)
|
||||
listbox.bind("<Key-Down>", self.down_event)
|
||||
# Mark as empty
|
||||
self.clear()
|
||||
|
||||
def close(self):
|
||||
self.frame.destroy()
|
||||
|
||||
def clear(self):
|
||||
self.listbox.delete(0, "end")
|
||||
self.empty = 1
|
||||
self.listbox.insert("end", self.default)
|
||||
|
||||
def append(self, item):
|
||||
if self.empty:
|
||||
self.listbox.delete(0, "end")
|
||||
self.empty = 0
|
||||
self.listbox.insert("end", str(item))
|
||||
|
||||
def get(self, index):
|
||||
return self.listbox.get(index)
|
||||
|
||||
def click_event(self, event):
|
||||
self.listbox.activate("@%d,%d" % (event.x, event.y))
|
||||
index = self.listbox.index("active")
|
||||
self.select(index)
|
||||
self.on_select(index)
|
||||
return "break"
|
||||
|
||||
def double_click_event(self, event):
|
||||
index = self.listbox.index("active")
|
||||
self.select(index)
|
||||
self.on_double(index)
|
||||
return "break"
|
||||
|
||||
menu = None
|
||||
|
||||
def popup_event(self, event):
|
||||
if not self.menu:
|
||||
self.make_menu()
|
||||
menu = self.menu
|
||||
self.listbox.activate("@%d,%d" % (event.x, event.y))
|
||||
index = self.listbox.index("active")
|
||||
self.select(index)
|
||||
menu.tk_popup(event.x_root, event.y_root)
|
||||
|
||||
def make_menu(self):
|
||||
menu = Menu(self.listbox, tearoff=0)
|
||||
self.menu = menu
|
||||
self.fill_menu()
|
||||
|
||||
def up_event(self, event):
|
||||
index = self.listbox.index("active")
|
||||
if self.listbox.selection_includes(index):
|
||||
index = index - 1
|
||||
else:
|
||||
index = self.listbox.size() - 1
|
||||
if index < 0:
|
||||
self.listbox.bell()
|
||||
else:
|
||||
self.select(index)
|
||||
self.on_select(index)
|
||||
return "break"
|
||||
|
||||
def down_event(self, event):
|
||||
index = self.listbox.index("active")
|
||||
if self.listbox.selection_includes(index):
|
||||
index = index + 1
|
||||
else:
|
||||
index = 0
|
||||
if index >= self.listbox.size():
|
||||
self.listbox.bell()
|
||||
else:
|
||||
self.select(index)
|
||||
self.on_select(index)
|
||||
return "break"
|
||||
|
||||
def select(self, index):
|
||||
self.listbox.focus_set()
|
||||
self.listbox.activate(index)
|
||||
self.listbox.selection_clear(0, "end")
|
||||
self.listbox.selection_set(index)
|
||||
self.listbox.see(index)
|
||||
|
||||
# Methods to override for specific actions
|
||||
|
||||
def fill_menu(self):
|
||||
pass
|
||||
|
||||
def on_select(self, index):
|
||||
pass
|
||||
|
||||
def on_double(self, index):
|
||||
pass
|
||||
|
||||
|
||||
def test():
|
||||
root = Tk()
|
||||
root.protocol("WM_DELETE_WINDOW", root.destroy)
|
||||
class MyScrolledList(ScrolledList):
|
||||
def fill_menu(self): self.menu.add_command(label="pass")
|
||||
def on_select(self, index): print "select", self.get(index)
|
||||
def on_double(self, index): print "double", self.get(index)
|
||||
s = MyScrolledList(root)
|
||||
for i in range(30):
|
||||
s.append("item %02d" % i)
|
||||
return root
|
||||
|
||||
def main():
|
||||
root = test()
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,97 +0,0 @@
|
|||
import tkSimpleDialog
|
||||
|
||||
###$ event <<find>>
|
||||
###$ win <Control-f>
|
||||
###$ unix <Control-u><Control-u><Control-s>
|
||||
|
||||
###$ event <<find-again>>
|
||||
###$ win <Control-g>
|
||||
###$ win <F3>
|
||||
###$ unix <Control-u><Control-s>
|
||||
|
||||
###$ event <<find-selection>>
|
||||
###$ win <Control-F3>
|
||||
###$ unix <Control-s>
|
||||
|
||||
###$ event <<find-in-files>>
|
||||
###$ win <Alt-F3>
|
||||
|
||||
###$ event <<replace>>
|
||||
###$ win <Control-h>
|
||||
|
||||
###$ event <<goto-line>>
|
||||
###$ win <Alt-g>
|
||||
###$ unix <Alt-g>
|
||||
|
||||
class SearchBinding:
|
||||
|
||||
windows_keydefs = {
|
||||
'<<find-again>>': ['<Control-g>', '<F3>'],
|
||||
'<<find-in-files>>': ['<Alt-F3>'],
|
||||
'<<find-selection>>': ['<Control-F3>'],
|
||||
'<<find>>': ['<Control-f>'],
|
||||
'<<replace>>': ['<Control-h>'],
|
||||
'<<goto-line>>': ['<Alt-g>'],
|
||||
}
|
||||
|
||||
unix_keydefs = {
|
||||
'<<find-again>>': ['<Control-u><Control-s>'],
|
||||
'<<find-in-files>>': ['<Alt-s>', '<Meta-s>'],
|
||||
'<<find-selection>>': ['<Control-s>'],
|
||||
'<<find>>': ['<Control-u><Control-u><Control-s>'],
|
||||
'<<replace>>': ['<Control-r>'],
|
||||
'<<goto-line>>': ['<Alt-g>', '<Meta-g>'],
|
||||
}
|
||||
|
||||
menudefs = [
|
||||
('edit', [
|
||||
None,
|
||||
('_Find...', '<<find>>'),
|
||||
('Find a_gain', '<<find-again>>'),
|
||||
('Find _selection', '<<find-selection>>'),
|
||||
('Find in Files...', '<<find-in-files>>'),
|
||||
('R_eplace...', '<<replace>>'),
|
||||
('Go to _line', '<<goto-line>>'),
|
||||
]),
|
||||
]
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
|
||||
def find_event(self, event):
|
||||
import SearchDialog
|
||||
SearchDialog.find(self.editwin.text)
|
||||
return "break"
|
||||
|
||||
def find_again_event(self, event):
|
||||
import SearchDialog
|
||||
SearchDialog.find_again(self.editwin.text)
|
||||
return "break"
|
||||
|
||||
def find_selection_event(self, event):
|
||||
import SearchDialog
|
||||
SearchDialog.find_selection(self.editwin.text)
|
||||
return "break"
|
||||
|
||||
def find_in_files_event(self, event):
|
||||
import GrepDialog
|
||||
GrepDialog.grep(self.editwin.text, self.editwin.io, self.editwin.flist)
|
||||
return "break"
|
||||
|
||||
def replace_event(self, event):
|
||||
import ReplaceDialog
|
||||
ReplaceDialog.replace(self.editwin.text)
|
||||
return "break"
|
||||
|
||||
def goto_line_event(self, event):
|
||||
text = self.editwin.text
|
||||
lineno = tkSimpleDialog.askinteger("Goto",
|
||||
"Go to line number:",
|
||||
parent=text)
|
||||
if lineno is None:
|
||||
return "break"
|
||||
if lineno <= 0:
|
||||
text.bell()
|
||||
return "break"
|
||||
text.mark_set("insert", "%d.0" % lineno)
|
||||
text.see("insert")
|
|
@ -1,67 +0,0 @@
|
|||
from Tkinter import *
|
||||
import SearchEngine
|
||||
from SearchDialogBase import SearchDialogBase
|
||||
|
||||
|
||||
def _setup(text):
|
||||
root = text._root()
|
||||
engine = SearchEngine.get(root)
|
||||
if not hasattr(engine, "_searchdialog"):
|
||||
engine._searchdialog = SearchDialog(root, engine)
|
||||
return engine._searchdialog
|
||||
|
||||
def find(text):
|
||||
return _setup(text).open(text)
|
||||
|
||||
def find_again(text):
|
||||
return _setup(text).find_again(text)
|
||||
|
||||
def find_selection(text):
|
||||
return _setup(text).find_selection(text)
|
||||
|
||||
class SearchDialog(SearchDialogBase):
|
||||
|
||||
def create_widgets(self):
|
||||
f = SearchDialogBase.create_widgets(self)
|
||||
self.make_button("Find", self.default_command, 1)
|
||||
|
||||
def default_command(self, event=None):
|
||||
if not self.engine.getprog():
|
||||
return
|
||||
if self.find_again(self.text):
|
||||
self.close()
|
||||
|
||||
def find_again(self, text):
|
||||
if not self.engine.getpat():
|
||||
self.open(text)
|
||||
return False
|
||||
if not self.engine.getprog():
|
||||
return False
|
||||
res = self.engine.search_text(text)
|
||||
if res:
|
||||
line, m = res
|
||||
i, j = m.span()
|
||||
first = "%d.%d" % (line, i)
|
||||
last = "%d.%d" % (line, j)
|
||||
try:
|
||||
selfirst = text.index("sel.first")
|
||||
sellast = text.index("sel.last")
|
||||
if selfirst == first and sellast == last:
|
||||
text.bell()
|
||||
return False
|
||||
except TclError:
|
||||
pass
|
||||
text.tag_remove("sel", "1.0", "end")
|
||||
text.tag_add("sel", first, last)
|
||||
text.mark_set("insert", self.engine.isback() and first or last)
|
||||
text.see("insert")
|
||||
return True
|
||||
else:
|
||||
text.bell()
|
||||
return False
|
||||
|
||||
def find_selection(self, text):
|
||||
pat = text.get("sel.first", "sel.last")
|
||||
if pat:
|
||||
self.engine.setcookedpat(pat)
|
||||
return self.find_again(text)
|
|
@ -1,128 +0,0 @@
|
|||
from Tkinter import *
|
||||
|
||||
class SearchDialogBase:
|
||||
|
||||
title = "Search Dialog"
|
||||
icon = "Search"
|
||||
needwrapbutton = 1
|
||||
|
||||
def __init__(self, root, engine):
|
||||
self.root = root
|
||||
self.engine = engine
|
||||
self.top = None
|
||||
|
||||
def open(self, text):
|
||||
self.text = text
|
||||
if not self.top:
|
||||
self.create_widgets()
|
||||
else:
|
||||
self.top.deiconify()
|
||||
self.top.tkraise()
|
||||
self.ent.focus_set()
|
||||
self.ent.selection_range(0, "end")
|
||||
self.ent.icursor(0)
|
||||
self.top.grab_set()
|
||||
|
||||
def close(self, event=None):
|
||||
if self.top:
|
||||
self.top.grab_release()
|
||||
self.top.withdraw()
|
||||
|
||||
def create_widgets(self):
|
||||
top = Toplevel(self.root)
|
||||
top.bind("<Return>", self.default_command)
|
||||
top.bind("<Escape>", self.close)
|
||||
top.protocol("WM_DELETE_WINDOW", self.close)
|
||||
top.wm_title(self.title)
|
||||
top.wm_iconname(self.icon)
|
||||
self.top = top
|
||||
|
||||
self.row = 0
|
||||
self.top.grid_columnconfigure(0, weight=0)
|
||||
self.top.grid_columnconfigure(1, weight=100)
|
||||
|
||||
self.create_entries()
|
||||
self.create_option_buttons()
|
||||
self.create_other_buttons()
|
||||
return self.create_command_buttons()
|
||||
|
||||
def make_entry(self, label, var):
|
||||
l = Label(self.top, text=label)
|
||||
l.grid(row=self.row, column=0, sticky="w")
|
||||
e = Entry(self.top, textvariable=var, exportselection=0)
|
||||
e.grid(row=self.row, column=1, sticky="we")
|
||||
self.row = self.row + 1
|
||||
return e
|
||||
|
||||
def make_frame(self):
|
||||
f = Frame(self.top)
|
||||
f.grid(row=self.row, column=0, columnspan=2, sticky="we")
|
||||
self.row = self.row + 1
|
||||
return f
|
||||
|
||||
def make_button(self, label, command, isdef=0, side="left"):
|
||||
b = Button(self.buttonframe,
|
||||
text=label, command=command,
|
||||
default=isdef and "active" or "normal")
|
||||
b.pack(side=side)
|
||||
return b
|
||||
|
||||
def create_entries(self):
|
||||
self.ent = self.make_entry("Find:", self.engine.patvar)
|
||||
|
||||
def create_option_buttons(self):
|
||||
f = self.make_frame()
|
||||
|
||||
btn = Checkbutton(f, anchor="w",
|
||||
variable=self.engine.revar,
|
||||
text="Regular expression")
|
||||
btn.pack(side="left", fill="both")
|
||||
if self.engine.isre():
|
||||
btn.select()
|
||||
|
||||
btn = Checkbutton(f, anchor="w",
|
||||
variable=self.engine.casevar,
|
||||
text="Match case")
|
||||
btn.pack(side="left", fill="both")
|
||||
if self.engine.iscase():
|
||||
btn.select()
|
||||
|
||||
btn = Checkbutton(f, anchor="w",
|
||||
variable=self.engine.wordvar,
|
||||
text="Whole word")
|
||||
btn.pack(side="left", fill="both")
|
||||
if self.engine.isword():
|
||||
btn.select()
|
||||
|
||||
if self.needwrapbutton:
|
||||
btn = Checkbutton(f, anchor="w",
|
||||
variable=self.engine.wrapvar,
|
||||
text="Wrap around")
|
||||
btn.pack(side="left", fill="both")
|
||||
if self.engine.iswrap():
|
||||
btn.select()
|
||||
|
||||
def create_other_buttons(self):
|
||||
f = self.make_frame()
|
||||
|
||||
lbl = Label(f, text="Direction: ")
|
||||
lbl.pack(side="left")
|
||||
|
||||
btn = Radiobutton(f, anchor="w",
|
||||
variable=self.engine.backvar, value=1,
|
||||
text="Up")
|
||||
btn.pack(side="left", fill="both")
|
||||
if self.engine.isback():
|
||||
btn.select()
|
||||
|
||||
btn = Radiobutton(f, anchor="w",
|
||||
variable=self.engine.backvar, value=0,
|
||||
text="Down")
|
||||
btn.pack(side="left", fill="both")
|
||||
if not self.engine.isback():
|
||||
btn.select()
|
||||
|
||||
def create_command_buttons(self):
|
||||
f = self.buttonframe = self.make_frame()
|
||||
b = self.make_button("close", self.close, side="right")
|
||||
b.lower()
|
|
@ -1,220 +0,0 @@
|
|||
import re
|
||||
from Tkinter import *
|
||||
import tkMessageBox
|
||||
|
||||
def get(root):
|
||||
if not hasattr(root, "_searchengine"):
|
||||
root._searchengine = SearchEngine(root)
|
||||
# XXX This will never garbage-collect -- who cares
|
||||
return root._searchengine
|
||||
|
||||
class SearchEngine:
|
||||
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
# State shared by search, replace, and grep;
|
||||
# the search dialogs bind these to UI elements.
|
||||
self.patvar = StringVar(root) # search pattern
|
||||
self.revar = BooleanVar(root) # regular expression?
|
||||
self.casevar = BooleanVar(root) # match case?
|
||||
self.wordvar = BooleanVar(root) # match whole word?
|
||||
self.wrapvar = BooleanVar(root) # wrap around buffer?
|
||||
self.wrapvar.set(1) # (on by default)
|
||||
self.backvar = BooleanVar(root) # search backwards?
|
||||
|
||||
# Access methods
|
||||
|
||||
def getpat(self):
|
||||
return self.patvar.get()
|
||||
|
||||
def setpat(self, pat):
|
||||
self.patvar.set(pat)
|
||||
|
||||
def isre(self):
|
||||
return self.revar.get()
|
||||
|
||||
def iscase(self):
|
||||
return self.casevar.get()
|
||||
|
||||
def isword(self):
|
||||
return self.wordvar.get()
|
||||
|
||||
def iswrap(self):
|
||||
return self.wrapvar.get()
|
||||
|
||||
def isback(self):
|
||||
return self.backvar.get()
|
||||
|
||||
# Higher level access methods
|
||||
|
||||
def getcookedpat(self):
|
||||
pat = self.getpat()
|
||||
if not self.isre():
|
||||
pat = re.escape(pat)
|
||||
if self.isword():
|
||||
pat = r"\b%s\b" % pat
|
||||
return pat
|
||||
|
||||
def getprog(self):
|
||||
pat = self.getpat()
|
||||
if not pat:
|
||||
self.report_error(pat, "Empty regular expression")
|
||||
return None
|
||||
pat = self.getcookedpat()
|
||||
flags = 0
|
||||
if not self.iscase():
|
||||
flags = flags | re.IGNORECASE
|
||||
try:
|
||||
prog = re.compile(pat, flags)
|
||||
except re.error, what:
|
||||
try:
|
||||
msg, col = what
|
||||
except:
|
||||
msg = str(what)
|
||||
col = -1
|
||||
self.report_error(pat, msg, col)
|
||||
return None
|
||||
return prog
|
||||
|
||||
def report_error(self, pat, msg, col=-1):
|
||||
# Derived class could overrid this with something fancier
|
||||
msg = "Error: " + str(msg)
|
||||
if pat:
|
||||
msg = msg + "\np\Pattern: " + str(pat)
|
||||
if col >= 0:
|
||||
msg = msg + "\nOffset: " + str(col)
|
||||
tkMessageBox.showerror("Regular expression error",
|
||||
msg, master=self.root)
|
||||
|
||||
def setcookedpat(self, pat):
|
||||
if self.isre():
|
||||
pat = re.escape(pat)
|
||||
self.setpat(pat)
|
||||
|
||||
def search_text(self, text, prog=None, ok=0):
|
||||
"""Search a text widget for the pattern.
|
||||
|
||||
If prog is given, it should be the precompiled pattern.
|
||||
Return a tuple (lineno, matchobj); None if not found.
|
||||
|
||||
This obeys the wrap and direction (back) settings.
|
||||
|
||||
The search starts at the selection (if there is one) or
|
||||
at the insert mark (otherwise). If the search is forward,
|
||||
it starts at the right of the selection; for a backward
|
||||
search, it starts at the left end. An empty match exactly
|
||||
at either end of the selection (or at the insert mark if
|
||||
there is no selection) is ignored unless the ok flag is true
|
||||
-- this is done to guarantee progress.
|
||||
|
||||
If the search is allowed to wrap around, it will return the
|
||||
original selection if (and only if) it is the only match.
|
||||
|
||||
"""
|
||||
if not prog:
|
||||
prog = self.getprog()
|
||||
if not prog:
|
||||
return None # Compilation failed -- stop
|
||||
wrap = self.wrapvar.get()
|
||||
first, last = get_selection(text)
|
||||
if self.isback():
|
||||
if ok:
|
||||
start = last
|
||||
else:
|
||||
start = first
|
||||
line, col = get_line_col(start)
|
||||
res = self.search_backward(text, prog, line, col, wrap, ok)
|
||||
else:
|
||||
if ok:
|
||||
start = first
|
||||
else:
|
||||
start = last
|
||||
line, col = get_line_col(start)
|
||||
res = self.search_forward(text, prog, line, col, wrap, ok)
|
||||
return res
|
||||
|
||||
def search_forward(self, text, prog, line, col, wrap, ok=0):
|
||||
wrapped = 0
|
||||
startline = line
|
||||
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||
while chars:
|
||||
m = prog.search(chars[:-1], col)
|
||||
if m:
|
||||
if ok or m.end() > col:
|
||||
return line, m
|
||||
line = line + 1
|
||||
if wrapped and line > startline:
|
||||
break
|
||||
col = 0
|
||||
ok = 1
|
||||
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||
if not chars and wrap:
|
||||
wrapped = 1
|
||||
wrap = 0
|
||||
line = 1
|
||||
chars = text.get("1.0", "2.0")
|
||||
return None
|
||||
|
||||
def search_backward(self, text, prog, line, col, wrap, ok=0):
|
||||
wrapped = 0
|
||||
startline = line
|
||||
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||
while 1:
|
||||
m = search_reverse(prog, chars[:-1], col)
|
||||
if m:
|
||||
if ok or m.start() < col:
|
||||
return line, m
|
||||
line = line - 1
|
||||
if wrapped and line < startline:
|
||||
break
|
||||
ok = 1
|
||||
if line <= 0:
|
||||
if not wrap:
|
||||
break
|
||||
wrapped = 1
|
||||
wrap = 0
|
||||
pos = text.index("end-1c")
|
||||
line, col = map(int, pos.split("."))
|
||||
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||
col = len(chars) - 1
|
||||
return None
|
||||
|
||||
# Helper to search backwards in a string.
|
||||
# (Optimized for the case where the pattern isn't found.)
|
||||
|
||||
def search_reverse(prog, chars, col):
|
||||
m = prog.search(chars)
|
||||
if not m:
|
||||
return None
|
||||
found = None
|
||||
i, j = m.span()
|
||||
while i < col and j <= col:
|
||||
found = m
|
||||
if i == j:
|
||||
j = j+1
|
||||
m = prog.search(chars, j)
|
||||
if not m:
|
||||
break
|
||||
i, j = m.span()
|
||||
return found
|
||||
|
||||
# Helper to get selection end points, defaulting to insert mark.
|
||||
# Return a tuple of indices ("line.col" strings).
|
||||
|
||||
def get_selection(text):
|
||||
try:
|
||||
first = text.index("sel.first")
|
||||
last = text.index("sel.last")
|
||||
except TclError:
|
||||
first = last = None
|
||||
if not first:
|
||||
first = text.index("insert")
|
||||
if not last:
|
||||
last = first
|
||||
return first, last
|
||||
|
||||
# Helper to parse a text index into a (line, col) tuple.
|
||||
|
||||
def get_line_col(index):
|
||||
line, col = map(int, index.split(".")) # Fails on invalid index
|
||||
return line, col
|
|
@ -1,92 +0,0 @@
|
|||
from Tkinter import *
|
||||
|
||||
class Separator:
|
||||
|
||||
def __init__(self, master, orient, min=10, thickness=5, bg=None):
|
||||
self.min = max(1, min)
|
||||
self.thickness = max(1, thickness)
|
||||
if orient in ("h", "horizontal"):
|
||||
self.side = "left"
|
||||
self.dim = "width"
|
||||
self.dir = "x"
|
||||
self.cursor = "sb_h_double_arrow"
|
||||
elif orient in ("v", "vertical"):
|
||||
self.side = "top"
|
||||
self.dim = "height"
|
||||
self.dir = "y"
|
||||
self.cursor = "sb_v_double_arrow"
|
||||
else:
|
||||
raise ValueError, "Separator: orient should be h or v"
|
||||
self.winfo_dim = "winfo_" + self.dim
|
||||
self.master = master = Frame(master)
|
||||
master.pack(expand=1, fill="both")
|
||||
self.f1 = Frame(master)
|
||||
self.f1.pack(expand=1, fill="both", side=self.side)
|
||||
self.div = Frame(master, cursor=self.cursor)
|
||||
self.div[self.dim] = self.thickness
|
||||
self.div.pack(fill="both", side=self.side)
|
||||
self.f2 = Frame(master)
|
||||
self.f2.pack(expand=1, fill="both", side=self.side)
|
||||
self.div.bind("<ButtonPress-1>", self.divider_press)
|
||||
if bg:
|
||||
##self.f1["bg"] = bg
|
||||
##self.f2["bg"] = bg
|
||||
self.div["bg"] = bg
|
||||
|
||||
def parts(self):
|
||||
return self.f1, self.f2
|
||||
|
||||
def divider_press(self, event):
|
||||
self.press_event = event
|
||||
self.f1.pack_propagate(0)
|
||||
self.f2.pack_propagate(0)
|
||||
for f in self.f1, self.f2:
|
||||
for dim in "width", "height":
|
||||
f[dim] = getattr(f, "winfo_"+dim)()
|
||||
self.div.bind("<Motion>", self.div_motion)
|
||||
self.div.bind("<ButtonRelease-1>", self.div_release)
|
||||
self.div.grab_set()
|
||||
|
||||
def div_motion(self, event):
|
||||
delta = getattr(event, self.dir) - getattr(self.press_event, self.dir)
|
||||
if delta:
|
||||
dim1 = getattr(self.f1, self.winfo_dim)()
|
||||
dim2 = getattr(self.f2, self.winfo_dim)()
|
||||
delta = max(delta, self.min-dim1)
|
||||
delta = min(delta, dim2-self.min)
|
||||
dim1 = dim1 + delta
|
||||
dim2 = dim2 - delta
|
||||
self.f1[self.dim] = dim1
|
||||
self.f2[self.dim] = dim2
|
||||
|
||||
def div_release(self, event):
|
||||
self.div_motion(event)
|
||||
self.div.unbind("<Motion>")
|
||||
self.div.grab_release()
|
||||
|
||||
class VSeparator(Separator):
|
||||
|
||||
def __init__(self, master, min=10, thickness=5, bg=None):
|
||||
Separator.__init__(self, master, "v", min, thickness, bg)
|
||||
|
||||
class HSeparator(Separator):
|
||||
|
||||
def __init__(self, master, min=10, thickness=5, bg=None):
|
||||
Separator.__init__(self, master, "h", min, thickness, bg)
|
||||
|
||||
def main():
|
||||
root = Tk()
|
||||
tlist = []
|
||||
outer = HSeparator(root, bg="red")
|
||||
for part in outer.parts():
|
||||
inner = VSeparator(part, bg="blue")
|
||||
for f in inner.parts():
|
||||
t = Text(f, width=40, height=10, borderwidth=0)
|
||||
t.pack(fill="both", expand=1)
|
||||
tlist.append(t)
|
||||
tlist[0].insert("1.0", "Make your own Mondrian!")
|
||||
tlist[1].insert("1.0", "Move the colored dividers...")
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,146 +0,0 @@
|
|||
import os
|
||||
import sys
|
||||
import linecache
|
||||
|
||||
from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
|
||||
from ObjectBrowser import ObjectTreeItem, make_objecttreeitem
|
||||
from OldStackViewer import StackViewer, NamespaceViewer
|
||||
|
||||
def StackBrowser(root, flist=None, tb=None, top=None):
|
||||
if top is None:
|
||||
from Tkinter import Toplevel
|
||||
top = Toplevel(root)
|
||||
sc = ScrolledCanvas(top, bg="white", highlightthickness=0)
|
||||
sc.frame.pack(expand=1, fill="both")
|
||||
item = StackTreeItem(flist, tb)
|
||||
node = TreeNode(sc.canvas, None, item)
|
||||
node.expand()
|
||||
|
||||
class StackTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, flist=None, tb=None):
|
||||
self.flist = flist
|
||||
self.stack = get_stack(tb)
|
||||
self.text = get_exception()
|
||||
|
||||
def GetText(self):
|
||||
return self.text
|
||||
|
||||
def GetSubList(self):
|
||||
sublist = []
|
||||
for info in self.stack:
|
||||
item = FrameTreeItem(info, self.flist)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
class FrameTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, info, flist):
|
||||
self.info = info
|
||||
self.flist = flist
|
||||
|
||||
def GetText(self):
|
||||
frame, lineno = self.info
|
||||
try:
|
||||
modname = frame.f_globals["__name__"]
|
||||
except:
|
||||
modname = "?"
|
||||
code = frame.f_code
|
||||
filename = code.co_filename
|
||||
funcname = code.co_name
|
||||
sourceline = linecache.getline(filename, lineno)
|
||||
sourceline = sourceline.strip()
|
||||
if funcname in ("?", "", None):
|
||||
item = "%s, line %d: %s" % (modname, lineno, sourceline)
|
||||
else:
|
||||
item = "%s.%s(...), line %d: %s" % (modname, funcname,
|
||||
lineno, sourceline)
|
||||
## if i == index:
|
||||
## item = "> " + item
|
||||
return item
|
||||
|
||||
def GetSubList(self):
|
||||
frame, lineno = self.info
|
||||
sublist = []
|
||||
if frame.f_globals is not frame.f_locals:
|
||||
item = VariablesTreeItem("<locals>", frame.f_locals, self.flist)
|
||||
sublist.append(item)
|
||||
item = VariablesTreeItem("<globals>", frame.f_globals, self.flist)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
def OnDoubleClick(self):
|
||||
if self.flist:
|
||||
frame, lineno = self.info
|
||||
filename = frame.f_code.co_filename
|
||||
if os.path.isfile(filename):
|
||||
self.flist.gotofileline(filename, lineno)
|
||||
|
||||
class VariablesTreeItem(ObjectTreeItem):
|
||||
|
||||
def GetText(self):
|
||||
return self.labeltext
|
||||
|
||||
def GetLabelText(self):
|
||||
return None
|
||||
|
||||
def IsExpandable(self):
|
||||
return len(self.object) > 0
|
||||
|
||||
def keys(self):
|
||||
return self.object.keys()
|
||||
|
||||
def GetSubList(self):
|
||||
sublist = []
|
||||
for key in self.keys():
|
||||
try:
|
||||
value = self.object[key]
|
||||
except KeyError:
|
||||
continue
|
||||
def setfunction(value, key=key, object=self.object):
|
||||
object[key] = value
|
||||
item = make_objecttreeitem(key + " =", value, setfunction)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
def get_stack(t=None, f=None):
|
||||
if t is None:
|
||||
t = sys.last_traceback
|
||||
stack = []
|
||||
if t and t.tb_frame is f:
|
||||
t = t.tb_next
|
||||
while f is not None:
|
||||
stack.append((f, f.f_lineno))
|
||||
if f is self.botframe:
|
||||
break
|
||||
f = f.f_back
|
||||
stack.reverse()
|
||||
while t is not None:
|
||||
stack.append((t.tb_frame, t.tb_lineno))
|
||||
t = t.tb_next
|
||||
return stack
|
||||
|
||||
def get_exception(type=None, value=None):
|
||||
if type is None:
|
||||
type = sys.last_type
|
||||
value = sys.last_value
|
||||
if hasattr(type, "__name__"):
|
||||
type = type.__name__
|
||||
s = str(type)
|
||||
if value is not None:
|
||||
s = s + ": " + str(value)
|
||||
return s
|
||||
|
||||
def _test():
|
||||
try:
|
||||
import testcode
|
||||
reload(testcode)
|
||||
except:
|
||||
sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info()
|
||||
from Tkinter import Tk
|
||||
root = Tk()
|
||||
StackBrowser(None, top=root)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
_test()
|
|
@ -1,211 +0,0 @@
|
|||
|
||||
TO DO:
|
||||
|
||||
- improve debugger:
|
||||
- manage breakpoints globally, allow bp deletion, tbreak, cbreak etc.
|
||||
- real object browser
|
||||
- help on how to use it (a simple help button will do wonders)
|
||||
- performance? (updates of large sets of locals are slow)
|
||||
- better integration of "debug module"
|
||||
- debugger should be global resource (attached to flist, not to shell)
|
||||
- fix the stupid bug where you need to step twice
|
||||
- display class name in stack viewer entries for methods
|
||||
- suppress tracing through IDLE internals (e.g. print)
|
||||
- add a button to suppress through a specific module or class or method
|
||||
- more object inspection to stack viewer, e.g. to view all array items
|
||||
- insert the initial current directory into sys.path
|
||||
- default directory attribute for each window instead of only for windows
|
||||
that have an associated filename
|
||||
- command expansion from keywords, module contents, other buffers, etc.
|
||||
- "Recent documents" menu item
|
||||
- Filter region command
|
||||
- Optional horizontal scroll bar
|
||||
- more Emacsisms:
|
||||
- ^K should cut to buffer
|
||||
- M-[, M-] to move by paragraphs
|
||||
- incremental search?
|
||||
- search should indicate wrap-around in some way
|
||||
- restructure state sensitive code to avoid testing flags all the time
|
||||
- persistent user state (e.g. window and cursor positions, bindings)
|
||||
- make backups when saving
|
||||
- check file mtimes at various points
|
||||
- Pluggable interface with RCS/CVS/Perforce/Clearcase
|
||||
- better help?
|
||||
- don't open second class browser on same module (nor second path browser)
|
||||
- unify class and path browsers
|
||||
- Need to define a standard way whereby one can determine one is running
|
||||
inside IDLE (needed for Tk mainloop, also handy for $PYTHONSTARTUP)
|
||||
- Add more utility methods for use by extensions (a la get_selection)
|
||||
- Way to run command in totally separate interpreter (fork+os.system?)
|
||||
- Way to find definition of fully-qualified name:
|
||||
In other words, select "UserDict.UserDict", hit some magic key and
|
||||
it loads up UserDict.py and finds the first def or class for UserDict.
|
||||
- need a way to force colorization on/off
|
||||
- need a way to force auto-indent on/off
|
||||
|
||||
Details:
|
||||
|
||||
- when there's a selection, left/right arrow should go to either
|
||||
end of the selection
|
||||
- ^O (on Unix -- open-line) should honor autoindent
|
||||
- after paste, show end of pasted text
|
||||
- on Windows, should turn short filename to long filename (not only in argv!)
|
||||
(shouldn't this be done -- or undone -- by ntpath.normpath?)
|
||||
- new autoindent after colon even indents when the colon is in a comment!
|
||||
- sometimes forward slashes in pathname remain
|
||||
- sometimes star in window name remains in Windows menu
|
||||
- With unix bindings, ESC by itself is ignored
|
||||
- Sometimes for no apparent reason a selection from the cursor to the
|
||||
end of the command buffer appears, which is hard to get rid of
|
||||
because it stays when you are typing!
|
||||
- The Line/Col in the status bar can be wrong initially in PyShell
|
||||
|
||||
Structural problems:
|
||||
|
||||
- too much knowledge in FileList about EditorWindow (for example)
|
||||
- should add some primitives for accessing the selection etc.
|
||||
to repeat cumbersome code over and over
|
||||
|
||||
======================================================================
|
||||
|
||||
Jeff Bauer suggests:
|
||||
|
||||
- Open Module doesn't appear to handle hierarchical packages.
|
||||
- Class browser should also allow hierarchical packages.
|
||||
- Open and Open Module could benefit from a history,
|
||||
either command line style, or Microsoft recent-file
|
||||
style.
|
||||
- Add a Smalltalk-style inspector (i.e. Tkinspect)
|
||||
|
||||
The last suggestion is already a reality, but not yet
|
||||
integrated into IDLE. I use a module called inspector.py,
|
||||
that used to be available from python.org(?) It no longer
|
||||
appears to be in the contributed section, and the source
|
||||
has no author attribution.
|
||||
|
||||
In any case, the code is useful for visually navigating
|
||||
an object's attributes, including its container hierarchy.
|
||||
|
||||
>>> from inspector import Tkinspect
|
||||
>>> Tkinspect(None, myObject)
|
||||
|
||||
Tkinspect could probably be extended and refined to
|
||||
integrate better into IDLE.
|
||||
|
||||
======================================================================
|
||||
|
||||
Comparison to PTUI
|
||||
------------------
|
||||
|
||||
+ PTUI's help is better (HTML!)
|
||||
|
||||
+ PTUI can attach a shell to any module
|
||||
|
||||
+ PTUI has some more I/O commands:
|
||||
open multiple
|
||||
append
|
||||
examine (what's that?)
|
||||
|
||||
======================================================================
|
||||
|
||||
Notes after trying to run Grail
|
||||
-------------------------------
|
||||
|
||||
- Grail does stuff to sys.path based on sys.argv[0]; you must set
|
||||
sys.argv[0] to something decent first (it is normally set to the path of
|
||||
the idle script).
|
||||
|
||||
- Grail must be exec'ed in __main__ because that's imported by some
|
||||
other parts of Grail.
|
||||
|
||||
- Grail uses a module called History and so does idle :-(
|
||||
|
||||
======================================================================
|
||||
|
||||
Robin Friedrich's items:
|
||||
|
||||
Things I'd like to see:
|
||||
- I'd like support for shift-click extending the selection. There's a
|
||||
bug now that it doesn't work the first time you try it.
|
||||
- Printing is needed. How hard can that be on Windows?
|
||||
- The python-mode trick of autoindenting a line with <tab> is neat and
|
||||
very handy.
|
||||
- (someday) a spellchecker for docstrings and comments.
|
||||
- a pagedown/up command key which moves to next class/def statement (top
|
||||
level)
|
||||
- split window capability
|
||||
- DnD text relocation/copying
|
||||
|
||||
Things I don't want to see.
|
||||
- line numbers... will probably slow things down way too much.
|
||||
- Please use another icon for the tree browser leaf. The small snake
|
||||
isn't cutting it.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
- Customizable views (multi-window or multi-pane). (Markus Gritsch)
|
||||
|
||||
- Being able to double click (maybe double right click) on a callable
|
||||
object in the editor which shows the source of the object, if
|
||||
possible. (Gerrit Holl)
|
||||
|
||||
- Hooks into the guts, like in Emacs. (Mike Romberg)
|
||||
|
||||
- Sharing the editor with a remote tutor. (Martijn Faassen)
|
||||
|
||||
- Multiple views on the same file. (Tony J Ibbs)
|
||||
|
||||
- Store breakpoints in a global (per-project) database (GvR); Dirk
|
||||
Heise adds: save some space-trimmed context and search around when
|
||||
reopening a file that might have been edited by someone else.
|
||||
|
||||
- Capture menu events in extensions without changing the IDLE source.
|
||||
(Matthias Barmeier)
|
||||
|
||||
- Use overlapping panels (a "notebook" in MFC terms I think) for info
|
||||
that doesn't need to be accessible simultaneously (e.g. HTML source
|
||||
and output). Use multi-pane windows for info that does need to be
|
||||
shown together (e.g. class browser and source). (Albert Brandl)
|
||||
|
||||
- A project should invisibly track all symbols, for instant search,
|
||||
replace and cross-ref. Projects should be allowed to span multiple
|
||||
directories, hosts, etc. Project management files are placed in a
|
||||
directory you specify. A global mapping between project names and
|
||||
project directories should exist [not so sure --GvR]. (Tim Peters)
|
||||
|
||||
- Merge attr-tips and auto-expand. (Mark Hammond, Tim Peters)
|
||||
|
||||
- Python Shell should behave more like a "shell window" as users know
|
||||
it -- i.e. you can only edit the current command, and the cursor can't
|
||||
escape from the command area. (Albert Brandl)
|
||||
|
||||
- Set X11 class to "idle/Idle", set icon and title to something
|
||||
beginning with "idle" -- for window manangers. (Randall Hopper)
|
||||
|
||||
- Config files editable through a preferences dialog. (me)
|
||||
|
||||
- Config files still editable outside the preferences dialog.
|
||||
(Randall Hopper)
|
||||
|
||||
- When you're editing a command in PyShell, and there are only blank
|
||||
lines below the cursor, hitting Return should ignore or delete those
|
||||
blank lines rather than deciding you're not on the last line. (me)
|
||||
|
||||
- Run command (F5 c.s.) should be more like Pythonwin's Run -- a
|
||||
dialog with options to give command line arguments, run the debugger,
|
||||
etc. (me)
|
||||
|
||||
- Shouldn't be able to delete part of the prompt (or any text before
|
||||
it) in the PyShell. (Martijn Faassen)
|
||||
|
||||
- Emacs style auto-fill (also smart about comments and strings).
|
||||
(Jeremy Hylton)
|
||||
|
||||
- Output of Run Script should go to a separate output window, not to
|
||||
the shell window. Output of separate runs should all go to the same
|
||||
window but clearly delimited. (David Scherer)
|
||||
|
||||
- GUI form designer to kick VB's butt. (Robert Geiger)
|
||||
|
||||
- Printing! Possibly via generation of PDF files which the user must
|
||||
then send to the printer separately. (Dinu Gherman)
|
|
@ -1,86 +0,0 @@
|
|||
# Ideas gleaned from PySol
|
||||
|
||||
from Tkinter import *
|
||||
|
||||
class ToolTipBase:
|
||||
|
||||
def __init__(self, button):
|
||||
self.button = button
|
||||
self.tipwindow = None
|
||||
self.id = None
|
||||
self.x = self.y = 0
|
||||
self._id1 = self.button.bind("<Enter>", self.enter)
|
||||
self._id2 = self.button.bind("<Leave>", self.leave)
|
||||
self._id3 = self.button.bind("<ButtonPress>", self.leave)
|
||||
|
||||
def enter(self, event=None):
|
||||
self.schedule()
|
||||
|
||||
def leave(self, event=None):
|
||||
self.unschedule()
|
||||
self.hidetip()
|
||||
|
||||
def schedule(self):
|
||||
self.unschedule()
|
||||
self.id = self.button.after(1500, self.showtip)
|
||||
|
||||
def unschedule(self):
|
||||
id = self.id
|
||||
self.id = None
|
||||
if id:
|
||||
self.button.after_cancel(id)
|
||||
|
||||
def showtip(self):
|
||||
if self.tipwindow:
|
||||
return
|
||||
# The tip window must be completely outside the button;
|
||||
# otherwise when the mouse enters the tip window we get
|
||||
# a leave event and it disappears, and then we get an enter
|
||||
# event and it reappears, and so on forever :-(
|
||||
x = self.button.winfo_rootx() + 20
|
||||
y = self.button.winfo_rooty() + self.button.winfo_height() + 1
|
||||
self.tipwindow = tw = Toplevel(self.button)
|
||||
tw.wm_overrideredirect(1)
|
||||
tw.wm_geometry("+%d+%d" % (x, y))
|
||||
self.showcontents()
|
||||
|
||||
def showcontents(self, text="Your text here"):
|
||||
# Override this in derived class
|
||||
label = Label(self.tipwindow, text=text, justify=LEFT,
|
||||
background="#ffffe0", relief=SOLID, borderwidth=1)
|
||||
label.pack()
|
||||
|
||||
def hidetip(self):
|
||||
tw = self.tipwindow
|
||||
self.tipwindow = None
|
||||
if tw:
|
||||
tw.destroy()
|
||||
|
||||
class ToolTip(ToolTipBase):
|
||||
def __init__(self, button, text):
|
||||
ToolTipBase.__init__(self, button)
|
||||
self.text = text
|
||||
def showcontents(self):
|
||||
ToolTipBase.showcontents(self, self.text)
|
||||
|
||||
class ListboxToolTip(ToolTipBase):
|
||||
def __init__(self, button, items):
|
||||
ToolTipBase.__init__(self, button)
|
||||
self.items = items
|
||||
def showcontents(self):
|
||||
listbox = Listbox(self.tipwindow, background="#ffffe0")
|
||||
listbox.pack()
|
||||
for item in self.items:
|
||||
listbox.insert(END, item)
|
||||
|
||||
def main():
|
||||
# Test code
|
||||
root = Tk()
|
||||
b = Button(root, text="Hello", command=root.destroy)
|
||||
b.pack()
|
||||
root.update()
|
||||
tip = ListboxToolTip(b, ["Hello", "world"])
|
||||
|
||||
# root.mainloop() # not in idle
|
||||
|
||||
main()
|
|
@ -1,476 +0,0 @@
|
|||
# XXX TO DO:
|
||||
# - popup menu
|
||||
# - support partial or total redisplay
|
||||
# - key bindings (instead of quick-n-dirty bindings on Canvas):
|
||||
# - up/down arrow keys to move focus around
|
||||
# - ditto for page up/down, home/end
|
||||
# - left/right arrows to expand/collapse & move out/in
|
||||
# - more doc strings
|
||||
# - add icons for "file", "module", "class", "method"; better "python" icon
|
||||
# - callback for selection???
|
||||
# - multiple-item selection
|
||||
# - tooltips
|
||||
# - redo geometry without magic numbers
|
||||
# - keep track of object ids to allow more careful cleaning
|
||||
# - optimize tree redraw after expand of subnode
|
||||
|
||||
import os
|
||||
import sys
|
||||
from Tkinter import *
|
||||
import imp
|
||||
|
||||
import ZoomHeight
|
||||
|
||||
ICONDIR = "Icons"
|
||||
|
||||
# Look for Icons subdirectory in the same directory as this module
|
||||
try:
|
||||
_icondir = os.path.join(os.path.dirname(__file__), ICONDIR)
|
||||
except NameError:
|
||||
_icondir = ICONDIR
|
||||
if os.path.isdir(_icondir):
|
||||
ICONDIR = _icondir
|
||||
elif not os.path.isdir(ICONDIR):
|
||||
raise RuntimeError, "can't find icon directory (%s)" % `ICONDIR`
|
||||
|
||||
def listicons(icondir=ICONDIR):
|
||||
"""Utility to display the available icons."""
|
||||
root = Tk()
|
||||
import glob
|
||||
list = glob.glob(os.path.join(icondir, "*.gif"))
|
||||
list.sort()
|
||||
images = []
|
||||
row = column = 0
|
||||
for file in list:
|
||||
name = os.path.splitext(os.path.basename(file))[0]
|
||||
image = PhotoImage(file=file, master=root)
|
||||
images.append(image)
|
||||
label = Label(root, image=image, bd=1, relief="raised")
|
||||
label.grid(row=row, column=column)
|
||||
label = Label(root, text=name)
|
||||
label.grid(row=row+1, column=column)
|
||||
column = column + 1
|
||||
if column >= 10:
|
||||
row = row+2
|
||||
column = 0
|
||||
root.images = images
|
||||
|
||||
|
||||
class TreeNode:
|
||||
|
||||
def __init__(self, canvas, parent, item):
|
||||
self.canvas = canvas
|
||||
self.parent = parent
|
||||
self.item = item
|
||||
self.state = 'collapsed'
|
||||
self.selected = 0
|
||||
self.children = []
|
||||
self.x = self.y = None
|
||||
self.iconimages = {} # cache of PhotoImage instances for icons
|
||||
|
||||
def destroy(self):
|
||||
for c in self.children[:]:
|
||||
self.children.remove(c)
|
||||
c.destroy()
|
||||
self.parent = None
|
||||
|
||||
def geticonimage(self, name):
|
||||
try:
|
||||
return self.iconimages[name]
|
||||
except KeyError:
|
||||
pass
|
||||
file, ext = os.path.splitext(name)
|
||||
ext = ext or ".gif"
|
||||
fullname = os.path.join(ICONDIR, file + ext)
|
||||
image = PhotoImage(master=self.canvas, file=fullname)
|
||||
self.iconimages[name] = image
|
||||
return image
|
||||
|
||||
def select(self, event=None):
|
||||
if self.selected:
|
||||
return
|
||||
self.deselectall()
|
||||
self.selected = 1
|
||||
self.canvas.delete(self.image_id)
|
||||
self.drawicon()
|
||||
self.drawtext()
|
||||
|
||||
def deselect(self, event=None):
|
||||
if not self.selected:
|
||||
return
|
||||
self.selected = 0
|
||||
self.canvas.delete(self.image_id)
|
||||
self.drawicon()
|
||||
self.drawtext()
|
||||
|
||||
def deselectall(self):
|
||||
if self.parent:
|
||||
self.parent.deselectall()
|
||||
else:
|
||||
self.deselecttree()
|
||||
|
||||
def deselecttree(self):
|
||||
if self.selected:
|
||||
self.deselect()
|
||||
for child in self.children:
|
||||
child.deselecttree()
|
||||
|
||||
def flip(self, event=None):
|
||||
if self.state == 'expanded':
|
||||
self.collapse()
|
||||
else:
|
||||
self.expand()
|
||||
self.item.OnDoubleClick()
|
||||
return "break"
|
||||
|
||||
def expand(self, event=None):
|
||||
if not self.item._IsExpandable():
|
||||
return
|
||||
if self.state != 'expanded':
|
||||
self.state = 'expanded'
|
||||
self.update()
|
||||
self.view()
|
||||
|
||||
def collapse(self, event=None):
|
||||
if self.state != 'collapsed':
|
||||
self.state = 'collapsed'
|
||||
self.update()
|
||||
|
||||
def view(self):
|
||||
top = self.y - 2
|
||||
bottom = self.lastvisiblechild().y + 17
|
||||
height = bottom - top
|
||||
visible_top = self.canvas.canvasy(0)
|
||||
visible_height = self.canvas.winfo_height()
|
||||
visible_bottom = self.canvas.canvasy(visible_height)
|
||||
if visible_top <= top and bottom <= visible_bottom:
|
||||
return
|
||||
x0, y0, x1, y1 = self.canvas._getints(self.canvas['scrollregion'])
|
||||
if top >= visible_top and height <= visible_height:
|
||||
fraction = top + height - visible_height
|
||||
else:
|
||||
fraction = top
|
||||
fraction = float(fraction) / y1
|
||||
self.canvas.yview_moveto(fraction)
|
||||
|
||||
def lastvisiblechild(self):
|
||||
if self.children and self.state == 'expanded':
|
||||
return self.children[-1].lastvisiblechild()
|
||||
else:
|
||||
return self
|
||||
|
||||
def update(self):
|
||||
if self.parent:
|
||||
self.parent.update()
|
||||
else:
|
||||
oldcursor = self.canvas['cursor']
|
||||
self.canvas['cursor'] = "watch"
|
||||
self.canvas.update()
|
||||
self.canvas.delete(ALL) # XXX could be more subtle
|
||||
self.draw(7, 2)
|
||||
x0, y0, x1, y1 = self.canvas.bbox(ALL)
|
||||
self.canvas.configure(scrollregion=(0, 0, x1, y1))
|
||||
self.canvas['cursor'] = oldcursor
|
||||
|
||||
def draw(self, x, y):
|
||||
# XXX This hard-codes too many geometry constants!
|
||||
self.x, self.y = x, y
|
||||
self.drawicon()
|
||||
self.drawtext()
|
||||
if self.state != 'expanded':
|
||||
return y+17
|
||||
# draw children
|
||||
if not self.children:
|
||||
sublist = self.item._GetSubList()
|
||||
if not sublist:
|
||||
# _IsExpandable() was mistaken; that's allowed
|
||||
return y+17
|
||||
for item in sublist:
|
||||
child = self.__class__(self.canvas, self, item)
|
||||
self.children.append(child)
|
||||
cx = x+20
|
||||
cy = y+17
|
||||
cylast = 0
|
||||
for child in self.children:
|
||||
cylast = cy
|
||||
self.canvas.create_line(x+9, cy+7, cx, cy+7, fill="gray50")
|
||||
cy = child.draw(cx, cy)
|
||||
if child.item._IsExpandable():
|
||||
if child.state == 'expanded':
|
||||
iconname = "minusnode"
|
||||
callback = child.collapse
|
||||
else:
|
||||
iconname = "plusnode"
|
||||
callback = child.expand
|
||||
image = self.geticonimage(iconname)
|
||||
id = self.canvas.create_image(x+9, cylast+7, image=image)
|
||||
# XXX This leaks bindings until canvas is deleted:
|
||||
self.canvas.tag_bind(id, "<1>", callback)
|
||||
self.canvas.tag_bind(id, "<Double-1>", lambda x: None)
|
||||
id = self.canvas.create_line(x+9, y+10, x+9, cylast+7,
|
||||
##stipple="gray50", # XXX Seems broken in Tk 8.0.x
|
||||
fill="gray50")
|
||||
self.canvas.tag_lower(id) # XXX .lower(id) before Python 1.5.2
|
||||
return cy
|
||||
|
||||
def drawicon(self):
|
||||
if self.selected:
|
||||
imagename = (self.item.GetSelectedIconName() or
|
||||
self.item.GetIconName() or
|
||||
"openfolder")
|
||||
else:
|
||||
imagename = self.item.GetIconName() or "folder"
|
||||
image = self.geticonimage(imagename)
|
||||
id = self.canvas.create_image(self.x, self.y, anchor="nw", image=image)
|
||||
self.image_id = id
|
||||
self.canvas.tag_bind(id, "<1>", self.select)
|
||||
self.canvas.tag_bind(id, "<Double-1>", self.flip)
|
||||
|
||||
def drawtext(self):
|
||||
textx = self.x+20-1
|
||||
texty = self.y-1
|
||||
labeltext = self.item.GetLabelText()
|
||||
if labeltext:
|
||||
id = self.canvas.create_text(textx, texty, anchor="nw",
|
||||
text=labeltext)
|
||||
self.canvas.tag_bind(id, "<1>", self.select)
|
||||
self.canvas.tag_bind(id, "<Double-1>", self.flip)
|
||||
x0, y0, x1, y1 = self.canvas.bbox(id)
|
||||
textx = max(x1, 200) + 10
|
||||
text = self.item.GetText() or "<no text>"
|
||||
try:
|
||||
self.entry
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
self.edit_finish()
|
||||
try:
|
||||
label = self.label
|
||||
except AttributeError:
|
||||
# padding carefully selected (on Windows) to match Entry widget:
|
||||
self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2)
|
||||
if self.selected:
|
||||
self.label.configure(fg="white", bg="darkblue")
|
||||
else:
|
||||
self.label.configure(fg="black", bg="white")
|
||||
id = self.canvas.create_window(textx, texty,
|
||||
anchor="nw", window=self.label)
|
||||
self.label.bind("<1>", self.select_or_edit)
|
||||
self.label.bind("<Double-1>", self.flip)
|
||||
self.text_id = id
|
||||
|
||||
def select_or_edit(self, event=None):
|
||||
if self.selected and self.item.IsEditable():
|
||||
self.edit(event)
|
||||
else:
|
||||
self.select(event)
|
||||
|
||||
def edit(self, event=None):
|
||||
self.entry = Entry(self.label, bd=0, highlightthickness=1, width=0)
|
||||
self.entry.insert(0, self.label['text'])
|
||||
self.entry.selection_range(0, END)
|
||||
self.entry.pack(ipadx=5)
|
||||
self.entry.focus_set()
|
||||
self.entry.bind("<Return>", self.edit_finish)
|
||||
self.entry.bind("<Escape>", self.edit_cancel)
|
||||
|
||||
def edit_finish(self, event=None):
|
||||
try:
|
||||
entry = self.entry
|
||||
del self.entry
|
||||
except AttributeError:
|
||||
return
|
||||
text = entry.get()
|
||||
entry.destroy()
|
||||
if text and text != self.item.GetText():
|
||||
self.item.SetText(text)
|
||||
text = self.item.GetText()
|
||||
self.label['text'] = text
|
||||
self.drawtext()
|
||||
self.canvas.focus_set()
|
||||
|
||||
def edit_cancel(self, event=None):
|
||||
try:
|
||||
entry = self.entry
|
||||
del self.entry
|
||||
except AttributeError:
|
||||
return
|
||||
entry.destroy()
|
||||
self.drawtext()
|
||||
self.canvas.focus_set()
|
||||
|
||||
|
||||
class TreeItem:
|
||||
|
||||
"""Abstract class representing tree items.
|
||||
|
||||
Methods should typically be overridden, otherwise a default action
|
||||
is used.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Constructor. Do whatever you need to do."""
|
||||
|
||||
def GetText(self):
|
||||
"""Return text string to display."""
|
||||
|
||||
def GetLabelText(self):
|
||||
"""Return label text string to display in front of text (if any)."""
|
||||
|
||||
expandable = None
|
||||
|
||||
def _IsExpandable(self):
|
||||
"""Do not override! Called by TreeNode."""
|
||||
if self.expandable is None:
|
||||
self.expandable = self.IsExpandable()
|
||||
return self.expandable
|
||||
|
||||
def IsExpandable(self):
|
||||
"""Return whether there are subitems."""
|
||||
return 1
|
||||
|
||||
def _GetSubList(self):
|
||||
"""Do not override! Called by TreeNode."""
|
||||
if not self.IsExpandable():
|
||||
return []
|
||||
sublist = self.GetSubList()
|
||||
if not sublist:
|
||||
self.expandable = 0
|
||||
return sublist
|
||||
|
||||
def IsEditable(self):
|
||||
"""Return whether the item's text may be edited."""
|
||||
|
||||
def SetText(self, text):
|
||||
"""Change the item's text (if it is editable)."""
|
||||
|
||||
def GetIconName(self):
|
||||
"""Return name of icon to be displayed normally."""
|
||||
|
||||
def GetSelectedIconName(self):
|
||||
"""Return name of icon to be displayed when selected."""
|
||||
|
||||
def GetSubList(self):
|
||||
"""Return list of items forming sublist."""
|
||||
|
||||
def OnDoubleClick(self):
|
||||
"""Called on a double-click on the item."""
|
||||
|
||||
|
||||
# Example application
|
||||
|
||||
class FileTreeItem(TreeItem):
|
||||
|
||||
"""Example TreeItem subclass -- browse the file system."""
|
||||
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
||||
def GetText(self):
|
||||
return os.path.basename(self.path) or self.path
|
||||
|
||||
def IsEditable(self):
|
||||
return os.path.basename(self.path) != ""
|
||||
|
||||
def SetText(self, text):
|
||||
newpath = os.path.dirname(self.path)
|
||||
newpath = os.path.join(newpath, text)
|
||||
if os.path.dirname(newpath) != os.path.dirname(self.path):
|
||||
return
|
||||
try:
|
||||
os.rename(self.path, newpath)
|
||||
self.path = newpath
|
||||
except os.error:
|
||||
pass
|
||||
|
||||
def GetIconName(self):
|
||||
if not self.IsExpandable():
|
||||
return "python" # XXX wish there was a "file" icon
|
||||
|
||||
def IsExpandable(self):
|
||||
return os.path.isdir(self.path)
|
||||
|
||||
def GetSubList(self):
|
||||
try:
|
||||
names = os.listdir(self.path)
|
||||
except os.error:
|
||||
return []
|
||||
names.sort(lambda a, b: cmp(os.path.normcase(a), os.path.normcase(b)))
|
||||
sublist = []
|
||||
for name in names:
|
||||
item = FileTreeItem(os.path.join(self.path, name))
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
|
||||
# A canvas widget with scroll bars and some useful bindings
|
||||
|
||||
class ScrolledCanvas:
|
||||
def __init__(self, master, **opts):
|
||||
if not opts.has_key('yscrollincrement'):
|
||||
opts['yscrollincrement'] = 17
|
||||
self.master = master
|
||||
self.frame = Frame(master)
|
||||
self.frame.rowconfigure(0, weight=1)
|
||||
self.frame.columnconfigure(0, weight=1)
|
||||
self.canvas = apply(Canvas, (self.frame,), opts)
|
||||
self.canvas.grid(row=0, column=0, sticky="nsew")
|
||||
self.vbar = Scrollbar(self.frame, name="vbar")
|
||||
self.vbar.grid(row=0, column=1, sticky="nse")
|
||||
self.hbar = Scrollbar(self.frame, name="hbar", orient="horizontal")
|
||||
self.hbar.grid(row=1, column=0, sticky="ews")
|
||||
self.canvas['yscrollcommand'] = self.vbar.set
|
||||
self.vbar['command'] = self.canvas.yview
|
||||
self.canvas['xscrollcommand'] = self.hbar.set
|
||||
self.hbar['command'] = self.canvas.xview
|
||||
self.canvas.bind("<Key-Prior>", self.page_up)
|
||||
self.canvas.bind("<Key-Next>", self.page_down)
|
||||
self.canvas.bind("<Key-Up>", self.unit_up)
|
||||
self.canvas.bind("<Key-Down>", self.unit_down)
|
||||
if isinstance(master, Toplevel) or isinstance(master, Tk):
|
||||
self.canvas.bind("<Alt-F2>", self.zoom_height)
|
||||
self.canvas.focus_set()
|
||||
def page_up(self, event):
|
||||
self.canvas.yview_scroll(-1, "page")
|
||||
return "break"
|
||||
def page_down(self, event):
|
||||
self.canvas.yview_scroll(1, "page")
|
||||
return "break"
|
||||
def unit_up(self, event):
|
||||
self.canvas.yview_scroll(-1, "unit")
|
||||
return "break"
|
||||
def unit_down(self, event):
|
||||
self.canvas.yview_scroll(1, "unit")
|
||||
return "break"
|
||||
def zoom_height(self, event):
|
||||
ZoomHeight.zoom_height(self.master)
|
||||
return "break"
|
||||
|
||||
|
||||
# Testing functions
|
||||
|
||||
def test():
|
||||
import PyShell
|
||||
root = Toplevel(PyShell.root)
|
||||
root.configure(bd=0, bg="yellow")
|
||||
root.focus_set()
|
||||
sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1)
|
||||
sc.frame.pack(expand=1, fill="both")
|
||||
item = FileTreeItem("C:/windows/desktop")
|
||||
node = TreeNode(sc.canvas, None, item)
|
||||
node.expand()
|
||||
|
||||
def test2():
|
||||
# test w/o scrolling canvas
|
||||
root = Tk()
|
||||
root.configure(bd=0)
|
||||
canvas = Canvas(root, bg="white", highlightthickness=0)
|
||||
canvas.pack(expand=1, fill="both")
|
||||
item = FileTreeItem(os.curdir)
|
||||
node = TreeNode(canvas, None, item)
|
||||
node.update()
|
||||
canvas.focus_set()
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
|
@ -1,352 +0,0 @@
|
|||
import sys
|
||||
import string
|
||||
from Tkinter import *
|
||||
from Delegator import Delegator
|
||||
|
||||
#$ event <<redo>>
|
||||
#$ win <Control-y>
|
||||
#$ unix <Alt-z>
|
||||
|
||||
#$ event <<undo>>
|
||||
#$ win <Control-z>
|
||||
#$ unix <Control-z>
|
||||
|
||||
#$ event <<dump-undo-state>>
|
||||
#$ win <Control-backslash>
|
||||
#$ unix <Control-backslash>
|
||||
|
||||
|
||||
class UndoDelegator(Delegator):
|
||||
|
||||
max_undo = 1000
|
||||
|
||||
def __init__(self):
|
||||
Delegator.__init__(self)
|
||||
self.reset_undo()
|
||||
|
||||
def setdelegate(self, delegate):
|
||||
if self.delegate is not None:
|
||||
self.unbind("<<undo>>")
|
||||
self.unbind("<<redo>>")
|
||||
self.unbind("<<dump-undo-state>>")
|
||||
Delegator.setdelegate(self, delegate)
|
||||
if delegate is not None:
|
||||
self.bind("<<undo>>", self.undo_event)
|
||||
self.bind("<<redo>>", self.redo_event)
|
||||
self.bind("<<dump-undo-state>>", self.dump_event)
|
||||
|
||||
def dump_event(self, event):
|
||||
from pprint import pprint
|
||||
pprint(self.undolist[:self.pointer])
|
||||
print "pointer:", self.pointer,
|
||||
print "saved:", self.saved,
|
||||
print "can_merge:", self.can_merge,
|
||||
print "get_saved():", self.get_saved()
|
||||
pprint(self.undolist[self.pointer:])
|
||||
return "break"
|
||||
|
||||
def reset_undo(self):
|
||||
self.was_saved = -1
|
||||
self.pointer = 0
|
||||
self.undolist = []
|
||||
self.undoblock = 0 # or a CommandSequence instance
|
||||
self.set_saved(1)
|
||||
|
||||
def set_saved(self, flag):
|
||||
if flag:
|
||||
self.saved = self.pointer
|
||||
else:
|
||||
self.saved = -1
|
||||
self.can_merge = 0
|
||||
self.check_saved()
|
||||
|
||||
def get_saved(self):
|
||||
return self.saved == self.pointer
|
||||
|
||||
saved_change_hook = None
|
||||
|
||||
def set_saved_change_hook(self, hook):
|
||||
self.saved_change_hook = hook
|
||||
|
||||
was_saved = -1
|
||||
|
||||
def check_saved(self):
|
||||
is_saved = self.get_saved()
|
||||
if is_saved != self.was_saved:
|
||||
self.was_saved = is_saved
|
||||
if self.saved_change_hook:
|
||||
self.saved_change_hook()
|
||||
|
||||
def insert(self, index, chars, tags=None):
|
||||
self.addcmd(InsertCommand(index, chars, tags))
|
||||
|
||||
def delete(self, index1, index2=None):
|
||||
self.addcmd(DeleteCommand(index1, index2))
|
||||
|
||||
# Clients should call undo_block_start() and undo_block_stop()
|
||||
# around a sequence of editing cmds to be treated as a unit by
|
||||
# undo & redo. Nested matching calls are OK, and the inner calls
|
||||
# then act like nops. OK too if no editing cmds, or only one
|
||||
# editing cmd, is issued in between: if no cmds, the whole
|
||||
# sequence has no effect; and if only one cmd, that cmd is entered
|
||||
# directly into the undo list, as if undo_block_xxx hadn't been
|
||||
# called. The intent of all that is to make this scheme easy
|
||||
# to use: all the client has to worry about is making sure each
|
||||
# _start() call is matched by a _stop() call.
|
||||
|
||||
def undo_block_start(self):
|
||||
if self.undoblock == 0:
|
||||
self.undoblock = CommandSequence()
|
||||
self.undoblock.bump_depth()
|
||||
|
||||
def undo_block_stop(self):
|
||||
if self.undoblock.bump_depth(-1) == 0:
|
||||
cmd = self.undoblock
|
||||
self.undoblock = 0
|
||||
if len(cmd) > 0:
|
||||
if len(cmd) == 1:
|
||||
# no need to wrap a single cmd
|
||||
cmd = cmd.getcmd(0)
|
||||
# this blk of cmds, or single cmd, has already
|
||||
# been done, so don't execute it again
|
||||
self.addcmd(cmd, 0)
|
||||
|
||||
def addcmd(self, cmd, execute=1):
|
||||
if execute:
|
||||
cmd.do(self.delegate)
|
||||
if self.undoblock != 0:
|
||||
self.undoblock.append(cmd)
|
||||
return
|
||||
if self.can_merge and self.pointer > 0:
|
||||
lastcmd = self.undolist[self.pointer-1]
|
||||
if lastcmd.merge(cmd):
|
||||
return
|
||||
self.undolist[self.pointer:] = [cmd]
|
||||
if self.saved > self.pointer:
|
||||
self.saved = -1
|
||||
self.pointer = self.pointer + 1
|
||||
if len(self.undolist) > self.max_undo:
|
||||
##print "truncating undo list"
|
||||
del self.undolist[0]
|
||||
self.pointer = self.pointer - 1
|
||||
if self.saved >= 0:
|
||||
self.saved = self.saved - 1
|
||||
self.can_merge = 1
|
||||
self.check_saved()
|
||||
|
||||
def undo_event(self, event):
|
||||
if self.pointer == 0:
|
||||
self.bell()
|
||||
return "break"
|
||||
cmd = self.undolist[self.pointer - 1]
|
||||
cmd.undo(self.delegate)
|
||||
self.pointer = self.pointer - 1
|
||||
self.can_merge = 0
|
||||
self.check_saved()
|
||||
return "break"
|
||||
|
||||
def redo_event(self, event):
|
||||
if self.pointer >= len(self.undolist):
|
||||
self.bell()
|
||||
return "break"
|
||||
cmd = self.undolist[self.pointer]
|
||||
cmd.redo(self.delegate)
|
||||
self.pointer = self.pointer + 1
|
||||
self.can_merge = 0
|
||||
self.check_saved()
|
||||
return "break"
|
||||
|
||||
|
||||
class Command:
|
||||
|
||||
# Base class for Undoable commands
|
||||
|
||||
tags = None
|
||||
|
||||
def __init__(self, index1, index2, chars, tags=None):
|
||||
self.marks_before = {}
|
||||
self.marks_after = {}
|
||||
self.index1 = index1
|
||||
self.index2 = index2
|
||||
self.chars = chars
|
||||
if tags:
|
||||
self.tags = tags
|
||||
|
||||
def __repr__(self):
|
||||
s = self.__class__.__name__
|
||||
t = (self.index1, self.index2, self.chars, self.tags)
|
||||
if self.tags is None:
|
||||
t = t[:-1]
|
||||
return s + `t`
|
||||
|
||||
def do(self, text):
|
||||
pass
|
||||
|
||||
def redo(self, text):
|
||||
pass
|
||||
|
||||
def undo(self, text):
|
||||
pass
|
||||
|
||||
def merge(self, cmd):
|
||||
return 0
|
||||
|
||||
def save_marks(self, text):
|
||||
marks = {}
|
||||
for name in text.mark_names():
|
||||
if name != "insert" and name != "current":
|
||||
marks[name] = text.index(name)
|
||||
return marks
|
||||
|
||||
def set_marks(self, text, marks):
|
||||
for name, index in marks.items():
|
||||
text.mark_set(name, index)
|
||||
|
||||
|
||||
class InsertCommand(Command):
|
||||
|
||||
# Undoable insert command
|
||||
|
||||
def __init__(self, index1, chars, tags=None):
|
||||
Command.__init__(self, index1, None, chars, tags)
|
||||
|
||||
def do(self, text):
|
||||
self.marks_before = self.save_marks(text)
|
||||
self.index1 = text.index(self.index1)
|
||||
if text.compare(self.index1, ">", "end-1c"):
|
||||
# Insert before the final newline
|
||||
self.index1 = text.index("end-1c")
|
||||
text.insert(self.index1, self.chars, self.tags)
|
||||
self.index2 = text.index("%s+%dc" % (self.index1, len(self.chars)))
|
||||
self.marks_after = self.save_marks(text)
|
||||
##sys.__stderr__.write("do: %s\n" % self)
|
||||
|
||||
def redo(self, text):
|
||||
text.mark_set('insert', self.index1)
|
||||
text.insert(self.index1, self.chars, self.tags)
|
||||
self.set_marks(text, self.marks_after)
|
||||
text.see('insert')
|
||||
##sys.__stderr__.write("redo: %s\n" % self)
|
||||
|
||||
def undo(self, text):
|
||||
text.mark_set('insert', self.index1)
|
||||
text.delete(self.index1, self.index2)
|
||||
self.set_marks(text, self.marks_before)
|
||||
text.see('insert')
|
||||
##sys.__stderr__.write("undo: %s\n" % self)
|
||||
|
||||
def merge(self, cmd):
|
||||
if self.__class__ is not cmd.__class__:
|
||||
return 0
|
||||
if self.index2 != cmd.index1:
|
||||
return 0
|
||||
if self.tags != cmd.tags:
|
||||
return 0
|
||||
if len(cmd.chars) != 1:
|
||||
return 0
|
||||
if self.chars and \
|
||||
self.classify(self.chars[-1]) != self.classify(cmd.chars):
|
||||
return 0
|
||||
self.index2 = cmd.index2
|
||||
self.chars = self.chars + cmd.chars
|
||||
return 1
|
||||
|
||||
alphanumeric = string.ascii_letters + string.digits + "_"
|
||||
|
||||
def classify(self, c):
|
||||
if c in self.alphanumeric:
|
||||
return "alphanumeric"
|
||||
if c == "\n":
|
||||
return "newline"
|
||||
return "punctuation"
|
||||
|
||||
|
||||
class DeleteCommand(Command):
|
||||
|
||||
# Undoable delete command
|
||||
|
||||
def __init__(self, index1, index2=None):
|
||||
Command.__init__(self, index1, index2, None, None)
|
||||
|
||||
def do(self, text):
|
||||
self.marks_before = self.save_marks(text)
|
||||
self.index1 = text.index(self.index1)
|
||||
if self.index2:
|
||||
self.index2 = text.index(self.index2)
|
||||
else:
|
||||
self.index2 = text.index(self.index1 + " +1c")
|
||||
if text.compare(self.index2, ">", "end-1c"):
|
||||
# Don't delete the final newline
|
||||
self.index2 = text.index("end-1c")
|
||||
self.chars = text.get(self.index1, self.index2)
|
||||
text.delete(self.index1, self.index2)
|
||||
self.marks_after = self.save_marks(text)
|
||||
##sys.__stderr__.write("do: %s\n" % self)
|
||||
|
||||
def redo(self, text):
|
||||
text.mark_set('insert', self.index1)
|
||||
text.delete(self.index1, self.index2)
|
||||
self.set_marks(text, self.marks_after)
|
||||
text.see('insert')
|
||||
##sys.__stderr__.write("redo: %s\n" % self)
|
||||
|
||||
def undo(self, text):
|
||||
text.mark_set('insert', self.index1)
|
||||
text.insert(self.index1, self.chars)
|
||||
self.set_marks(text, self.marks_before)
|
||||
text.see('insert')
|
||||
##sys.__stderr__.write("undo: %s\n" % self)
|
||||
|
||||
class CommandSequence(Command):
|
||||
|
||||
# Wrapper for a sequence of undoable cmds to be undone/redone
|
||||
# as a unit
|
||||
|
||||
def __init__(self):
|
||||
self.cmds = []
|
||||
self.depth = 0
|
||||
|
||||
def __repr__(self):
|
||||
s = self.__class__.__name__
|
||||
strs = []
|
||||
for cmd in self.cmds:
|
||||
strs.append(" " + `cmd`)
|
||||
return s + "(\n" + ",\n".join(strs) + "\n)"
|
||||
|
||||
def __len__(self):
|
||||
return len(self.cmds)
|
||||
|
||||
def append(self, cmd):
|
||||
self.cmds.append(cmd)
|
||||
|
||||
def getcmd(self, i):
|
||||
return self.cmds[i]
|
||||
|
||||
def redo(self, text):
|
||||
for cmd in self.cmds:
|
||||
cmd.redo(text)
|
||||
|
||||
def undo(self, text):
|
||||
cmds = self.cmds[:]
|
||||
cmds.reverse()
|
||||
for cmd in cmds:
|
||||
cmd.undo(text)
|
||||
|
||||
def bump_depth(self, incr=1):
|
||||
self.depth = self.depth + incr
|
||||
return self.depth
|
||||
|
||||
def main():
|
||||
from Percolator import Percolator
|
||||
root = Tk()
|
||||
root.wm_protocol("WM_DELETE_WINDOW", root.quit)
|
||||
text = Text()
|
||||
text.pack()
|
||||
text.focus_set()
|
||||
p = Percolator(text)
|
||||
d = UndoDelegator()
|
||||
p.insertfilter(d)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,92 +0,0 @@
|
|||
from Tkinter import *
|
||||
|
||||
|
||||
class WidgetRedirector:
|
||||
|
||||
"""Support for redirecting arbitrary widget subcommands."""
|
||||
|
||||
def __init__(self, widget):
|
||||
self.dict = {}
|
||||
self.widget = widget
|
||||
self.tk = tk = widget.tk
|
||||
w = widget._w
|
||||
self.orig = w + "_orig"
|
||||
tk.call("rename", w, self.orig)
|
||||
tk.createcommand(w, self.dispatch)
|
||||
|
||||
def __repr__(self):
|
||||
return "WidgetRedirector(%s<%s>)" % (self.widget.__class__.__name__,
|
||||
self.widget._w)
|
||||
|
||||
def close(self):
|
||||
for name in self.dict.keys():
|
||||
self.unregister(name)
|
||||
widget = self.widget; del self.widget
|
||||
orig = self.orig; del self.orig
|
||||
tk = widget.tk
|
||||
w = widget._w
|
||||
tk.deletecommand(w)
|
||||
tk.call("rename", orig, w)
|
||||
|
||||
def register(self, name, function):
|
||||
if self.dict.has_key(name):
|
||||
previous = dict[name]
|
||||
else:
|
||||
previous = OriginalCommand(self, name)
|
||||
self.dict[name] = function
|
||||
setattr(self.widget, name, function)
|
||||
return previous
|
||||
|
||||
def unregister(self, name):
|
||||
if self.dict.has_key(name):
|
||||
function = self.dict[name]
|
||||
del self.dict[name]
|
||||
if hasattr(self.widget, name):
|
||||
delattr(self.widget, name)
|
||||
return function
|
||||
else:
|
||||
return None
|
||||
|
||||
def dispatch(self, cmd, *args):
|
||||
m = self.dict.get(cmd)
|
||||
try:
|
||||
if m:
|
||||
return apply(m, args)
|
||||
else:
|
||||
return self.tk.call((self.orig, cmd) + args)
|
||||
except TclError:
|
||||
return ""
|
||||
|
||||
|
||||
class OriginalCommand:
|
||||
|
||||
def __init__(self, redir, name):
|
||||
self.redir = redir
|
||||
self.name = name
|
||||
self.tk = redir.tk
|
||||
self.orig = redir.orig
|
||||
self.tk_call = self.tk.call
|
||||
self.orig_and_name = (self.orig, self.name)
|
||||
|
||||
def __repr__(self):
|
||||
return "OriginalCommand(%s, %s)" % (`self.redir`, `self.name`)
|
||||
|
||||
def __call__(self, *args):
|
||||
return self.tk_call(self.orig_and_name + args)
|
||||
|
||||
|
||||
def main():
|
||||
root = Tk()
|
||||
text = Text()
|
||||
text.pack()
|
||||
text.focus_set()
|
||||
redir = WidgetRedirector(text)
|
||||
global orig_insert
|
||||
def my_insert(*args):
|
||||
print "insert", args
|
||||
apply(orig_insert, args)
|
||||
orig_insert = redir.register("insert", my_insert)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,85 +0,0 @@
|
|||
from Tkinter import *
|
||||
|
||||
class WindowList:
|
||||
|
||||
def __init__(self):
|
||||
self.dict = {}
|
||||
self.callbacks = []
|
||||
|
||||
def add(self, window):
|
||||
window.after_idle(self.call_callbacks)
|
||||
self.dict[str(window)] = window
|
||||
|
||||
def delete(self, window):
|
||||
try:
|
||||
del self.dict[str(window)]
|
||||
except KeyError:
|
||||
# Sometimes, destroy() is called twice
|
||||
pass
|
||||
self.call_callbacks()
|
||||
|
||||
def add_windows_to_menu(self, menu):
|
||||
list = []
|
||||
for key in self.dict.keys():
|
||||
window = self.dict[key]
|
||||
try:
|
||||
title = window.get_title()
|
||||
except TclError:
|
||||
continue
|
||||
list.append((title, window))
|
||||
list.sort()
|
||||
for title, window in list:
|
||||
if title == "Python Shell":
|
||||
# Hack -- until we have a better way to this
|
||||
continue
|
||||
menu.add_command(label=title, command=window.wakeup)
|
||||
|
||||
def register_callback(self, callback):
|
||||
self.callbacks.append(callback)
|
||||
|
||||
def unregister_callback(self, callback):
|
||||
try:
|
||||
self.callbacks.remove(callback)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def call_callbacks(self):
|
||||
for callback in self.callbacks:
|
||||
try:
|
||||
callback()
|
||||
except:
|
||||
print "warning: callback failed in WindowList", \
|
||||
sys.exc_type, ":", sys.exc_value
|
||||
|
||||
registry = WindowList()
|
||||
|
||||
add_windows_to_menu = registry.add_windows_to_menu
|
||||
register_callback = registry.register_callback
|
||||
unregister_callback = registry.unregister_callback
|
||||
|
||||
|
||||
class ListedToplevel(Toplevel):
|
||||
|
||||
def __init__(self, master, **kw):
|
||||
Toplevel.__init__(self, master, kw)
|
||||
registry.add(self)
|
||||
|
||||
def destroy(self):
|
||||
registry.delete(self)
|
||||
Toplevel.destroy(self)
|
||||
|
||||
def get_title(self):
|
||||
# Subclass can override
|
||||
return self.wm_title()
|
||||
|
||||
def wakeup(self):
|
||||
try:
|
||||
if self.wm_state() == "iconic":
|
||||
self.wm_deiconify()
|
||||
else:
|
||||
self.tkraise()
|
||||
self.focus_set()
|
||||
except TclError:
|
||||
# This can happen when the window menu was torn off.
|
||||
# Simply ignore it.
|
||||
pass
|
|
@ -1,46 +0,0 @@
|
|||
# Sample extension: zoom a window to maximum height
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
class ZoomHeight:
|
||||
|
||||
menudefs = [
|
||||
('windows', [
|
||||
('_Zoom Height', '<<zoom-height>>'),
|
||||
])
|
||||
]
|
||||
|
||||
windows_keydefs = {
|
||||
'<<zoom-height>>': ['<Alt-F2>'],
|
||||
}
|
||||
unix_keydefs = {
|
||||
'<<zoom-height>>': ['<Control-x><Control-z>'],
|
||||
}
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
|
||||
def zoom_height_event(self, event):
|
||||
top = self.editwin.top
|
||||
zoom_height(top)
|
||||
|
||||
def zoom_height(top):
|
||||
geom = top.wm_geometry()
|
||||
m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
|
||||
if not m:
|
||||
top.bell()
|
||||
return
|
||||
width, height, x, y = map(int, m.groups())
|
||||
newheight = top.winfo_screenheight()
|
||||
if sys.platform == 'win32':
|
||||
newy = 0
|
||||
newheight = newheight - 72
|
||||
else:
|
||||
newy = 24
|
||||
newheight = newheight - 96
|
||||
if height >= newheight:
|
||||
newgeom = ""
|
||||
else:
|
||||
newgeom = "%dx%d+%d+%d" % (width, newheight, x, newy)
|
||||
top.wm_geometry(newgeom)
|
|
@ -1 +0,0 @@
|
|||
# Dummy file to make this a potential package.
|
|
@ -1,3 +0,0 @@
|
|||
[EditorWindow]
|
||||
font-name= monaco
|
||||
font-size= 9
|
|
@ -1,4 +0,0 @@
|
|||
[EditorWindow]
|
||||
font-name= courier
|
||||
font-size= 10
|
||||
print-command=lpr %s
|
|
@ -1,4 +0,0 @@
|
|||
[EditorWindow]
|
||||
font-name: courier new
|
||||
font-size: 10
|
||||
print-command=start /min notepad /p %s
|
|
@ -1,64 +0,0 @@
|
|||
# IDLE reads several config files to determine user preferences. This
|
||||
# file is the default config file. When IDLE starts, it will look in
|
||||
# the following four files in order:
|
||||
# config.txt the default config file
|
||||
# config-[win/unix/mac].txt the generic platform config file
|
||||
# config-[sys.platform].txt the specific platform config file
|
||||
# ~/.idle the user config file
|
||||
# XXX what about Windows?
|
||||
#
|
||||
# The last definition of each option is used. For example, you can
|
||||
# override the default window size (80x24) by defining width and
|
||||
# height options in the EditorWindow section of your ~/.idle file
|
||||
#
|
||||
# IDLE extensions can be enabled and disabled by adding them to one of
|
||||
# the config files. To enable an extension, create a section with the
|
||||
# same name as the extension, e.g. the [ParenMatch] section below. To
|
||||
# disable an extension, either remove the section or add the 'enable'
|
||||
# option with the value 0.
|
||||
|
||||
[EditorWindow]
|
||||
width= 80
|
||||
height= 24
|
||||
# fonts defined in config-[win/unix].txt
|
||||
|
||||
[Colors]
|
||||
normal-foreground= black
|
||||
normal-background= white
|
||||
# These color types are not explicitly defined= sync, todo, stdin
|
||||
keyword-foreground= #ff7700
|
||||
comment-foreground= #dd0000
|
||||
string-foreground= #00aa00
|
||||
definition-foreground= #0000ff
|
||||
hilite-foreground= #000068
|
||||
hilite-background= #006868
|
||||
break-foreground= #ff7777
|
||||
hit-foreground= #ffffff
|
||||
hit-background= #000000
|
||||
stdout-foreground= blue
|
||||
stderr-foreground= red
|
||||
console-foreground= #770000
|
||||
error-background= #ff7777
|
||||
cursor-background= black
|
||||
|
||||
[SearchBinding]
|
||||
|
||||
[AutoIndent]
|
||||
|
||||
[AutoExpand]
|
||||
|
||||
[FormatParagraph]
|
||||
|
||||
[ZoomHeight]
|
||||
|
||||
[ScriptBinding]
|
||||
|
||||
[CallTips]
|
||||
|
||||
[ParenMatch]
|
||||
enable= 0
|
||||
style= expression
|
||||
flash-delay= 500
|
||||
bell= 1
|
||||
hilite-foreground= black
|
||||
hilite-background= #43cd80
|
|
@ -1,89 +0,0 @@
|
|||
#! /usr/bin/env python
|
||||
|
||||
"""Parse event definitions out of comments in source files."""
|
||||
|
||||
import sys
|
||||
import glob
|
||||
import fileinput
|
||||
import pprint
|
||||
|
||||
def main():
|
||||
hits = []
|
||||
sublist = []
|
||||
args = sys.argv[1:]
|
||||
if not args:
|
||||
args = filter(lambda s: 'A' <= s[0] <= 'Z', glob.glob("*.py"))
|
||||
if not args:
|
||||
print "No arguments, no [A-Z]*.py files."
|
||||
return 1
|
||||
for line in fileinput.input(args):
|
||||
if line[:2] == '#$':
|
||||
if not sublist:
|
||||
sublist.append('file %s' % fileinput.filename())
|
||||
sublist.append('line %d' % fileinput.lineno())
|
||||
sublist.append(line[2:-1].strip())
|
||||
else:
|
||||
if sublist:
|
||||
hits.append(sublist)
|
||||
sublist = []
|
||||
if sublist:
|
||||
hits.append(sublist)
|
||||
sublist = []
|
||||
dd = {}
|
||||
for sublist in hits:
|
||||
d = {}
|
||||
for line in sublist:
|
||||
words = line.split(None, 1)
|
||||
if len(words) != 2:
|
||||
continue
|
||||
tag = words[0]
|
||||
l = d.get(tag, [])
|
||||
l.append(words[1])
|
||||
d[tag] = l
|
||||
if d.has_key('event'):
|
||||
keys = d['event']
|
||||
if len(keys) != 1:
|
||||
print "Multiple event keys in", d
|
||||
print 'File "%s", line %d' % (d['file'], d['line'])
|
||||
key = keys[0]
|
||||
if dd.has_key(key):
|
||||
print "Duplicate event in", d
|
||||
print 'File "%s", line %d' % (d['file'], d['line'])
|
||||
return
|
||||
dd[key] = d
|
||||
else:
|
||||
print "No event key in", d
|
||||
print 'File "%s", line %d' % (d['file'], d['line'])
|
||||
winevents = getevents(dd, "win")
|
||||
unixevents = getevents(dd, "unix")
|
||||
save = sys.stdout
|
||||
f = open("keydefs.py", "w")
|
||||
try:
|
||||
sys.stdout = f
|
||||
print "windows_keydefs = \\"
|
||||
pprint.pprint(winevents)
|
||||
print
|
||||
print "unix_keydefs = \\"
|
||||
pprint.pprint(unixevents)
|
||||
finally:
|
||||
sys.stdout = save
|
||||
f.close()
|
||||
|
||||
def getevents(dd, key):
|
||||
res = {}
|
||||
events = dd.keys()
|
||||
events.sort()
|
||||
for e in events:
|
||||
d = dd[e]
|
||||
if d.has_key(key) or d.has_key("all"):
|
||||
list = []
|
||||
for x in d.get(key, []) + d.get("all", []):
|
||||
list.append(x)
|
||||
if key == "unix" and x[:5] == "<Alt-":
|
||||
x = "<Meta-" + x[5:]
|
||||
list.append(x)
|
||||
res[e] = list
|
||||
return res
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
|
@ -1,120 +0,0 @@
|
|||
Writing an IDLE extension
|
||||
|
||||
An IDLE extension can define new key bindings and menu entries for IDLE
|
||||
edit windows. There is a simple mechanism to load extensions when IDLE
|
||||
starts up and to attach them to each edit window. (It is also possible
|
||||
to make other changes to IDLE, but this must be done by editing the IDLE
|
||||
source code.)
|
||||
|
||||
The list of extensions loaded at startup time is configured by editing
|
||||
the file config.txt; see below for details.
|
||||
|
||||
An IDLE extension is defined by a class. Methods of the class define
|
||||
actions that are invoked by those bindings or menu entries. Class (or
|
||||
instance) variables define the bindings and menu additions; these are
|
||||
automatically applied by IDLE when the extension is linked to an edit
|
||||
window.
|
||||
|
||||
An IDLE extension class is instantiated with a single argument,
|
||||
`editwin', an EditorWindow instance. The extension cannot assume much
|
||||
about this argument, but it is guarateed to have the following instance
|
||||
variables:
|
||||
|
||||
text a Text instance (a widget)
|
||||
io an IOBinding instance (more about this later)
|
||||
flist the FileList instance (shared by all edit windows)
|
||||
|
||||
(There are a few more, but they are rarely useful.)
|
||||
|
||||
The extension class must not bind key events. Rather, it must define
|
||||
one or more virtual events, e.g. <<zoom-height>>, and corresponding
|
||||
methods, e.g. zoom_height_event(), and have one or more class (or instance)
|
||||
variables that define mappings between virtual events and key sequences,
|
||||
e.g. <Alt-F2>. When the extension is loaded, these key sequences will
|
||||
be bound to the corresponding virtual events, and the virtual events
|
||||
will be bound to the corresponding methods. (This indirection is done
|
||||
so that the key bindings can easily be changed, and so that other
|
||||
sources of virtual events can exist, such as menu entries.)
|
||||
|
||||
The following class or instance variables are used to define key
|
||||
bindings for virtual events:
|
||||
|
||||
keydefs for all platforms
|
||||
mac_keydefs for Macintosh
|
||||
windows_keydefs for Windows
|
||||
unix_keydefs for Unix (and other platforms)
|
||||
|
||||
Each of these variables, if it exists, must be a dictionary whose
|
||||
keys are virtual events, and whose values are lists of key sequences.
|
||||
|
||||
An extension can define menu entries in a similar fashion. This is done
|
||||
with a class or instance variable named menudefs; it should be a list of
|
||||
pair, where each pair is a menu name (lowercase) and a list of menu
|
||||
entries. Each menu entry is either None (to insert a separator entry) or
|
||||
a pair of strings (menu_label, virtual_event). Here, menu_label is the
|
||||
label of the menu entry, and virtual_event is the virtual event to be
|
||||
generated when the entry is selected. An underscore in the menu label
|
||||
is removed; the character following the underscore is displayed
|
||||
underlined, to indicate the shortcut character (for Windows).
|
||||
|
||||
At the moment, extensions cannot define whole new menus; they must
|
||||
define entries in existing menus. Some menus are not present on some
|
||||
windows; such entry definitions are then ignored, but the key bindings
|
||||
are still applied. (This should probably be refined in the future.)
|
||||
|
||||
Here is a complete example example:
|
||||
|
||||
class ZoomHeight:
|
||||
|
||||
menudefs = [
|
||||
('edit', [
|
||||
None, # Separator
|
||||
('_Zoom Height', '<<zoom-height>>'),
|
||||
])
|
||||
]
|
||||
|
||||
windows_keydefs = {
|
||||
'<<zoom-height>>': ['<Alt-F2>'],
|
||||
}
|
||||
unix_keydefs = {
|
||||
'<<zoom-height>>': ['<Control-z><Control-z>'],
|
||||
}
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
|
||||
def zoom_height_event(self, event):
|
||||
"...Do what you want here..."
|
||||
|
||||
The final piece of the puzzle is the file "config.txt", which is used
|
||||
to to configure the loading of extensions. For each extension,
|
||||
you must include a section in config.txt (or in any of the other
|
||||
configuration files that are consulted at startup: config-unix.txt,
|
||||
config-win.txt, or ~/.idle). A section is headed by the module name
|
||||
in square brackets, e.g.
|
||||
|
||||
[ZoomHeight]
|
||||
|
||||
The section may be empty, or it may define configuration options for
|
||||
the extension. (See ParenMatch.py for an example.) A special option
|
||||
is 'enable': including
|
||||
|
||||
enable = 0
|
||||
|
||||
in a section disables that extension. More than one configuration
|
||||
file may specify options for the same extension, so a user may disable
|
||||
an extension that is loaded by default, or enable an extension that is
|
||||
disabled by default.
|
||||
|
||||
Extensions can define key bindings and menu entries that reference
|
||||
events they don't implement (including standard events); however this is
|
||||
not recommended (and may be forbidden in the future).
|
||||
|
||||
Extensions are not required to define menu entries for all events they
|
||||
implement.
|
||||
|
||||
Note: in order to change key bindings, you must currently edit the file
|
||||
keydefs. It contains two dictionaries named and formatted like the
|
||||
keydefs dictionaries described above, one for the Unix bindings and one
|
||||
for the Windows bindings. In the future, a better mechanism will be
|
||||
provided.
|
|
@ -1,157 +0,0 @@
|
|||
[See end for tips.]
|
||||
|
||||
Click on the dotted line at the top of a menu to "tear it off": a
|
||||
separate window containing the menu is created.
|
||||
|
||||
File menu:
|
||||
|
||||
New window -- create a new editing window
|
||||
Open... -- open an existing file
|
||||
Open module... -- open an existing module (searches sys.path)
|
||||
Class browser -- show classes and methods in current file
|
||||
Path browser -- show sys.path directories, modules, classes
|
||||
and methods
|
||||
---
|
||||
Save -- save current window to the associated file (unsaved
|
||||
windows have a * before and after the window title)
|
||||
|
||||
Save As... -- save current window to new file, which becomes
|
||||
the associated file
|
||||
Save Copy As... -- save current window to different file
|
||||
without changing the associated file
|
||||
---
|
||||
Close -- close current window (asks to save if unsaved)
|
||||
Exit -- close all windows and quit IDLE (asks to save if unsaved)
|
||||
|
||||
Edit menu:
|
||||
|
||||
Undo -- Undo last change to current window (max 1000 changes)
|
||||
Redo -- Redo last undone change to current window
|
||||
---
|
||||
Cut -- Copy selection into system-wide clipboard; then delete selection
|
||||
Copy -- Copy selection into system-wide clipboard
|
||||
Paste -- Insert system-wide clipboard into window
|
||||
Select All -- Select the entire contents of the edit buffer
|
||||
---
|
||||
Find... -- Open a search dialog box with many options
|
||||
Find again -- Repeat last search
|
||||
Find selection -- Search for the string in the selection
|
||||
Find in Files... -- Open a search dialog box for searching files
|
||||
Replace... -- Open a search-and-replace dialog box
|
||||
Go to line -- Ask for a line number and show that line
|
||||
---
|
||||
Indent region -- Shift selected lines right 4 spaces
|
||||
Dedent region -- Shift selected lines left 4 spaces
|
||||
Comment out region -- Insert ## in front of selected lines
|
||||
Uncomment region -- Remove leading # or ## from selected lines
|
||||
Tabify region -- Turns *leading* stretches of spaces into tabs
|
||||
Untabify region -- Turn *all* tabs into the right number of spaces
|
||||
Expand word -- Expand the word you have typed to match another
|
||||
word in the same buffer; repeat to get a different expansion
|
||||
Format Paragraph -- Reformat the current blank-line-separated paragraph
|
||||
---
|
||||
Import module -- Import or reload the current module
|
||||
Run script -- Execute the current file in the __main__ namespace
|
||||
|
||||
Windows menu:
|
||||
|
||||
Zoom Height -- toggles the window between normal size (24x80)
|
||||
and maximum height.
|
||||
---
|
||||
The rest of this menu lists the names of all open windows;
|
||||
select one to bring it to the foreground (deiconifying it if
|
||||
necessary).
|
||||
|
||||
Debug menu (in the Python Shell window only):
|
||||
|
||||
Go to file/line -- look around the insert point for a filename
|
||||
and linenumber, open the file, and show the line
|
||||
Open stack viewer -- show the stack traceback of the last exception
|
||||
Debugger toggle -- Run commands in the shell under the debugger
|
||||
JIT Stack viewer toggle -- Open stack viewer on traceback
|
||||
|
||||
Basic editing and navigation:
|
||||
|
||||
Backspace deletes to the left; DEL deletes to the right
|
||||
Arrow keys and Page Up/Down to move around
|
||||
Home/End go to begin/end of line
|
||||
Control-Home/End go to begin/end of file
|
||||
Some Emacs bindings may also work, e.g. ^B/^P/^A/^E/^D/^L
|
||||
|
||||
Automatic indentation:
|
||||
|
||||
After a block-opening statement, the next line is indented by
|
||||
4 spaces (in the Python Shell window by one tab). After
|
||||
certain keywords (break, return etc.) the next line is
|
||||
dedented. In leading indentation, Backspace deletes up to 4
|
||||
spaces if they are there. Tab inserts 1-4 spaces (in the
|
||||
Python Shell window one tab). See also the indent/dedent
|
||||
region commands in the edit menu.
|
||||
|
||||
Python Shell window:
|
||||
|
||||
^C interrupts executing command
|
||||
^D sends end-of-file; closes window if typed at >>> prompt
|
||||
|
||||
Command history:
|
||||
|
||||
Alt-p retrieves previous command matching what you have typed
|
||||
Alt-n retrieves next
|
||||
Return while on any previous command retrieves that command
|
||||
Alt-/ (Expand word) is also useful here
|
||||
|
||||
Syntax colors:
|
||||
|
||||
The coloring is applied in a background "thread", so you may
|
||||
occasionally see uncolorized text. To change the color
|
||||
scheme, edit the [Colors] section in config.txt (or add a
|
||||
[Colors] section to ~/.idle).
|
||||
|
||||
Python syntax colors:
|
||||
|
||||
Keywords orange
|
||||
Strings green
|
||||
Comments red
|
||||
Definitions blue
|
||||
|
||||
Shell colors:
|
||||
|
||||
Console output brown
|
||||
stdout blue
|
||||
stderr dark green
|
||||
stdin black
|
||||
|
||||
Other preferences:
|
||||
|
||||
Most preferences can be changed by editing one of the
|
||||
configuration text files: config.txt (generic) or one of
|
||||
config-unix.txt, config-win.txt, config.mac.txt (platform
|
||||
specific). User-specific preferences can be stored in
|
||||
$HOME/.idle, which overrides the config*.txt files.
|
||||
|
||||
To change keyboard bindings, edit Bindings.py
|
||||
|
||||
Command line usage:
|
||||
|
||||
idle.py [-c command] [-d] [-e] [-s] [-t title] [arg] ...
|
||||
|
||||
-c command run this command
|
||||
-d enable debugger
|
||||
-e edit mode; arguments are files to be edited
|
||||
-s run $IDLESTARTUP or $PYTHONSTARTUP first
|
||||
-t title set title of shell window
|
||||
|
||||
If there are arguments:
|
||||
|
||||
If -e is used, arguments are files opened for editing and
|
||||
sys.argv reflects the arguments passed to IDLE itself.
|
||||
|
||||
Otherwise, if -c is used, all arguments are placed in
|
||||
sys.argv[1:...], with sys.argv[0] set to '-c'.
|
||||
|
||||
Otherwise, if neither -e nor -c is used, the first
|
||||
argument is a script which is executed with the remaining
|
||||
arguments in sys.argv[1:...] and sys.argv[0] set to the
|
||||
script name. If the script name is '-', no script is
|
||||
executed but an interactive Python session is started; the
|
||||
arguments are still available in sys.argv.
|
|
@ -1,12 +0,0 @@
|
|||
#! /usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
from idlelib import IdleConf
|
||||
|
||||
idle_dir = os.path.dirname(IdleConf.__file__)
|
||||
IdleConf.load(idle_dir)
|
||||
|
||||
# defer importing Pyshell until IdleConf is loaded
|
||||
from idlelib import PyShell
|
||||
PyShell.main()
|
|
@ -1,3 +0,0 @@
|
|||
rem idle.bat
|
||||
|
||||
start idle.pyw %1 %2 %3 %4 %5 %6 %7 %8 %9
|
|
@ -1,4 +0,0 @@
|
|||
#! /usr/bin/env python
|
||||
|
||||
import PyShell
|
||||
PyShell.main()
|
|
@ -1,8 +0,0 @@
|
|||
try:
|
||||
import idle
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
raw_input("Hit return to exit...")
|
|
@ -1 +0,0 @@
|
|||
IDLE_VERSION = "0.8"
|
|
@ -1,57 +0,0 @@
|
|||
windows_keydefs = \
|
||||
{'<<Copy>>': ['<Control-c>', '<Control-C>'],
|
||||
'<<Cut>>': ['<Control-x>', '<Control-X>'],
|
||||
'<<Paste>>': ['<Control-v>', '<Control-V>'],
|
||||
'<<beginning-of-line>>': ['<Control-a>', '<Home>'],
|
||||
'<<center-insert>>': ['<Control-l>'],
|
||||
'<<close-all-windows>>': ['<Control-q>'],
|
||||
'<<close-window>>': ['<Alt-F4>'],
|
||||
'<<dump-undo-state>>': ['<Control-backslash>'],
|
||||
'<<end-of-file>>': ['<Control-d>'],
|
||||
'<<help>>': ['<F1>'],
|
||||
'<<history-next>>': ['<Alt-n>'],
|
||||
'<<history-previous>>': ['<Alt-p>'],
|
||||
'<<interrupt-execution>>': ['<Control-c>'],
|
||||
'<<open-class-browser>>': ['<Alt-c>'],
|
||||
'<<open-module>>': ['<Alt-m>'],
|
||||
'<<open-new-window>>': ['<Control-n>'],
|
||||
'<<open-window-from-file>>': ['<Control-o>'],
|
||||
'<<plain-newline-and-indent>>': ['<Control-j>'],
|
||||
'<<print-window>>': ['<Control-p>'],
|
||||
'<<redo>>': ['<Control-y>'],
|
||||
'<<remove-selection>>': ['<Escape>'],
|
||||
'<<save-copy-of-window-as-file>>': ['<Alt-Shift-s>'],
|
||||
'<<save-window-as-file>>': ['<Alt-s>'],
|
||||
'<<save-window>>': ['<Control-s>'],
|
||||
'<<select-all>>': ['<Control-a>'],
|
||||
'<<toggle-auto-coloring>>': ['<Control-slash>'],
|
||||
'<<undo>>': ['<Control-z>']}
|
||||
|
||||
unix_keydefs = \
|
||||
{'<<Copy>>': ['<Alt-w>', '<Meta-w>'],
|
||||
'<<Cut>>': ['<Control-w>'],
|
||||
'<<Paste>>': ['<Control-y>'],
|
||||
'<<beginning-of-line>>': ['<Control-a>', '<Home>'],
|
||||
'<<center-insert>>': ['<Control-l>'],
|
||||
'<<close-all-windows>>': ['<Control-x><Control-c>'],
|
||||
'<<close-window>>': ['<Control-x><Control-0>', '<Control-x><Key-0>'],
|
||||
'<<do-nothing>>': ['<Control-x>'],
|
||||
'<<dump-undo-state>>': ['<Control-backslash>'],
|
||||
'<<end-of-file>>': ['<Control-d>'],
|
||||
'<<help>>': ['<F1>'],
|
||||
'<<history-next>>': ['<Alt-n>', '<Meta-n>'],
|
||||
'<<history-previous>>': ['<Alt-p>', '<Meta-p>'],
|
||||
'<<interrupt-execution>>': ['<Control-c>'],
|
||||
'<<open-class-browser>>': ['<Control-x><Control-b>'],
|
||||
'<<open-module>>': ['<Control-x><Control-m>'],
|
||||
'<<open-new-window>>': ['<Control-x><Control-n>'],
|
||||
'<<open-window-from-file>>': ['<Control-x><Control-f>'],
|
||||
'<<plain-newline-and-indent>>': ['<Control-j>'],
|
||||
'<<print-window>>': ['<Control-x><Control-p>'],
|
||||
'<<redo>>': ['<Alt-z>', '<Meta-z>'],
|
||||
'<<save-copy-of-window-as-file>>': ['<Control-x><w>'],
|
||||
'<<save-window-as-file>>': ['<Control-x><Control-w>'],
|
||||
'<<save-window>>': ['<Control-x><Control-s>'],
|
||||
'<<select-all>>': ['<Alt-a>', '<Meta-a>'],
|
||||
'<<toggle-auto-coloring>>': ['<Control-slash>'],
|
||||
'<<undo>>': ['<Control-z>']}
|
|
@ -1,100 +0,0 @@
|
|||
import os, glob, sys
|
||||
from distutils.core import setup
|
||||
from distutils.command.build_py import build_py
|
||||
from distutils.command.install_lib import install_lib
|
||||
import idlever
|
||||
|
||||
try:
|
||||
pos = sys.argv.index("--check-tkinter")
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
del sys.argv[pos]
|
||||
try:
|
||||
import _tkinter
|
||||
except ImportError:
|
||||
print >>sys.stderr, "Cannot install IDLE without _tkinter"
|
||||
raise SystemExit
|
||||
|
||||
try:
|
||||
package_dir = os.path.join(os.environ["SRCDIR"], "Tools", "idle")
|
||||
except KeyError:
|
||||
package_dir = "."
|
||||
|
||||
# name of idle package
|
||||
idlelib = "idlelib"
|
||||
|
||||
# the normal build_py would not incorporate the .txt files
|
||||
txt_files = ['config-unix.txt','config-win.txt','config.txt', 'help.txt']
|
||||
Icons = glob.glob1(os.path.join(package_dir,"Icons"),"*.gif")
|
||||
class idle_build_py(build_py):
|
||||
def get_plain_outfile(self, build_dir, package, file):
|
||||
# like get_module_outfile, but does not append .py
|
||||
outfile_path = [build_dir] + list(package) + [file]
|
||||
return apply(os.path.join, outfile_path)
|
||||
|
||||
def run(self):
|
||||
# Copies all .py files, then also copies the txt and gif files
|
||||
build_py.run(self)
|
||||
assert self.packages == [idlelib]
|
||||
for name in txt_files:
|
||||
outfile = self.get_plain_outfile(self.build_lib, [idlelib], name)
|
||||
dir = os.path.dirname(outfile)
|
||||
self.mkpath(dir)
|
||||
self.copy_file(os.path.join(package_dir, name), outfile,
|
||||
preserve_mode = 0)
|
||||
for name in Icons:
|
||||
outfile = self.get_plain_outfile(self.build_lib,
|
||||
[idlelib,"Icons"], name)
|
||||
dir = os.path.dirname(outfile)
|
||||
self.mkpath(dir)
|
||||
self.copy_file(os.path.join(package_dir, "Icons", name),
|
||||
outfile, preserve_mode = 0)
|
||||
|
||||
def get_source_files(self):
|
||||
# returns the .py files, the .txt files, and the icons
|
||||
icons = [os.path.join(package_dir, "Icons",name) for name in Icons]
|
||||
txts = [os.path.join(package_dir, name) for name in txt_files]
|
||||
return build_py.get_source_files(self)+txt_files+icons
|
||||
|
||||
def get_outputs(self, include_bytecode=1):
|
||||
# returns the built files
|
||||
outputs = build_py.get_outputs(self, include_bytecode)
|
||||
if not include_bytecode:
|
||||
return outputs
|
||||
for name in txt_files:
|
||||
filename = self.get_plain_outfile(self.build_lib,
|
||||
[idlelib], name)
|
||||
outputs.append(filename)
|
||||
for name in Icons:
|
||||
filename = self.get_plain_outfile(self.build_lib,
|
||||
[idlelib,"Icons"], name)
|
||||
outputs.append(filename)
|
||||
return outputs
|
||||
|
||||
# Arghhh. install_lib thinks that all files returned from build_py's
|
||||
# get_outputs are bytecode files
|
||||
class idle_install_lib(install_lib):
|
||||
def _bytecode_filenames(self, files):
|
||||
files = [n for n in files if n.endswith('.py')]
|
||||
return install_lib._bytecode_filenames(self,files)
|
||||
|
||||
|
||||
setup(name="IDLE",
|
||||
version = idlever.IDLE_VERSION,
|
||||
description = "IDLE, the Python IDE",
|
||||
author = "Guido van Rossum",
|
||||
author_email = "guido@python.org",
|
||||
#url =
|
||||
long_description =
|
||||
"""IDLE is a Tkinter based IDE for Python. It is written in 100% pure
|
||||
Python and works both on Windows and Unix. It features a multi-window
|
||||
text editor with multiple undo, Python colorizing, and many other things,
|
||||
as well as a Python shell window and a debugger.""",
|
||||
|
||||
cmdclass = {'build_py':idle_build_py,
|
||||
'install_lib':idle_install_lib},
|
||||
package_dir = {idlelib: package_dir},
|
||||
packages = [idlelib],
|
||||
scripts = [os.path.join(package_dir, 'idle')]
|
||||
)
|
|
@ -1,31 +0,0 @@
|
|||
import string
|
||||
|
||||
def f():
|
||||
a = 0
|
||||
b = 1
|
||||
c = 2
|
||||
d = 3
|
||||
e = 4
|
||||
g()
|
||||
|
||||
def g():
|
||||
h()
|
||||
|
||||
def h():
|
||||
i()
|
||||
|
||||
def i():
|
||||
j()
|
||||
|
||||
def j():
|
||||
k()
|
||||
|
||||
def k():
|
||||
l()
|
||||
|
||||
l = lambda: test()
|
||||
|
||||
def test():
|
||||
string.capwords(1)
|
||||
|
||||
f()
|
Loading…
Reference in New Issue