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