mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Checking in IDLE 0.2.
Much has changed -- too much, in fact, to write down. The big news is that there's a standard way to write IDLE extensions; see extend.txt. Some sample extensions have been provided, and some existing code has been converted to extensions. Probably the biggest new user feature is a new search dialog with more options, search and replace, and even search in files (grep). This is exactly as downloaded from my laptop after returning from the holidays -- it hasn't even been tested on Unix yet.
This commit is contained in:
parent
f07c328c07
commit
504b0bf066
38 changed files with 2204 additions and 899 deletions
|
@ -1,17 +1,30 @@
|
||||||
import string
|
import string
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
###$ event <<expand-word>>
|
||||||
|
###$ win <Alt-slash>
|
||||||
|
###$ unix <Alt-slash>
|
||||||
|
|
||||||
class AutoExpand:
|
class AutoExpand:
|
||||||
|
|
||||||
|
keydefs = {
|
||||||
|
'<<expand-word>>': ['<Alt-slash>'],
|
||||||
|
}
|
||||||
|
|
||||||
|
menudefs = [
|
||||||
|
('edit', [
|
||||||
|
('E_xpand word', '<<expand-word>>'),
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
|
||||||
wordchars = string.letters + string.digits + "_"
|
wordchars = string.letters + string.digits + "_"
|
||||||
|
|
||||||
def __init__(self, text):
|
def __init__(self, editwin):
|
||||||
self.text = text
|
self.text = editwin.text
|
||||||
self.text.wordlist = None
|
self.text.wordlist = None # XXX what is this?
|
||||||
self.state = None
|
self.state = None
|
||||||
self.text.bind("<<expand-word>>", self.autoexpand)
|
|
||||||
|
|
||||||
def autoexpand(self, event):
|
def expand_word_event(self, event):
|
||||||
curinsert = self.text.index("insert")
|
curinsert = self.text.index("insert")
|
||||||
curline = self.text.get("insert linestart", "insert lineend")
|
curline = self.text.get("insert linestart", "insert lineend")
|
||||||
if not self.state:
|
if not self.state:
|
||||||
|
|
|
@ -1,16 +1,81 @@
|
||||||
import string
|
import string
|
||||||
|
from Tkinter import TclError
|
||||||
|
|
||||||
|
###$ 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>
|
||||||
|
|
||||||
class AutoIndent:
|
class AutoIndent:
|
||||||
|
|
||||||
def __init__(self, text, prefertabs=0, spaceindent=4*" "):
|
menudefs = [
|
||||||
self.text = text
|
('edit', [
|
||||||
self.prefertabs = prefertabs
|
None,
|
||||||
self.spaceindent = spaceindent
|
('_Indent region', '<<indent-region>>'),
|
||||||
text.bind("<<newline-and-indent>>", self.autoindent)
|
('_Dedent region', '<<dedent-region>>'),
|
||||||
text.bind("<<indent-region>>", self.indentregion)
|
('Comment _out region', '<<comment-region>>'),
|
||||||
text.bind("<<dedent-region>>", self.dedentregion)
|
('U_ncomment region', '<<uncomment-region>>'),
|
||||||
text.bind("<<comment-region>>", self.commentregion)
|
('Tabify region', '<<tabify-region>>'),
|
||||||
text.bind("<<uncomment-region>>", self.uncommentregion)
|
('Untabify region', '<<untabify-region>>'),
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
|
||||||
|
windows_keydefs = {
|
||||||
|
'<<newline-and-indent>>': ['<Key-Return>', '<KP_Enter>'],
|
||||||
|
'<<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>'],
|
||||||
|
}
|
||||||
|
|
||||||
|
unix_keydefs = {
|
||||||
|
'<<newline-and-indent>>': ['<Key-Return>', '<KP_Enter>'],
|
||||||
|
'<<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>'],
|
||||||
|
}
|
||||||
|
|
||||||
|
prefertabs = 0
|
||||||
|
spaceindent = 4*" "
|
||||||
|
|
||||||
|
def __init__(self, editwin):
|
||||||
|
self.text = editwin.text
|
||||||
|
|
||||||
def config(self, **options):
|
def config(self, **options):
|
||||||
for key, value in options.items():
|
for key, value in options.items():
|
||||||
|
@ -21,8 +86,16 @@ class AutoIndent:
|
||||||
else:
|
else:
|
||||||
raise KeyError, "bad option name: %s" % `key`
|
raise KeyError, "bad option name: %s" % `key`
|
||||||
|
|
||||||
def autoindent(self, event):
|
def newline_and_indent_event(self, event):
|
||||||
text = self.text
|
text = self.text
|
||||||
|
try:
|
||||||
|
first = text.index("sel.first")
|
||||||
|
last = text.index("sel.last")
|
||||||
|
except TclError:
|
||||||
|
first = last = None
|
||||||
|
if first and last:
|
||||||
|
text.delete(first, last)
|
||||||
|
text.mark_set("insert", first)
|
||||||
line = text.get("insert linestart", "insert")
|
line = text.get("insert linestart", "insert")
|
||||||
i, n = 0, len(line)
|
i, n = 0, len(line)
|
||||||
while i < n and line[i] in " \t":
|
while i < n and line[i] in " \t":
|
||||||
|
@ -43,8 +116,10 @@ class AutoIndent:
|
||||||
text.see("insert")
|
text.see("insert")
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
def indentregion(self, event):
|
auto_indent = newline_and_indent_event
|
||||||
head, tail, chars, lines = self.getregion()
|
|
||||||
|
def indent_region_event(self, event):
|
||||||
|
head, tail, chars, lines = self.get_region()
|
||||||
for pos in range(len(lines)):
|
for pos in range(len(lines)):
|
||||||
line = lines[pos]
|
line = lines[pos]
|
||||||
if line:
|
if line:
|
||||||
|
@ -53,11 +128,11 @@ class AutoIndent:
|
||||||
i = i+1
|
i = i+1
|
||||||
line = line[:i] + " " + line[i:]
|
line = line[:i] + " " + line[i:]
|
||||||
lines[pos] = line
|
lines[pos] = line
|
||||||
self.setregion(head, tail, chars, lines)
|
self.set_region(head, tail, chars, lines)
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
def dedentregion(self, event):
|
def dedent_region_event(self, event):
|
||||||
head, tail, chars, lines = self.getregion()
|
head, tail, chars, lines = self.get_region()
|
||||||
for pos in range(len(lines)):
|
for pos in range(len(lines)):
|
||||||
line = lines[pos]
|
line = lines[pos]
|
||||||
if line:
|
if line:
|
||||||
|
@ -75,20 +150,20 @@ class AutoIndent:
|
||||||
indent = indent[:-4]
|
indent = indent[:-4]
|
||||||
line = indent + line
|
line = indent + line
|
||||||
lines[pos] = line
|
lines[pos] = line
|
||||||
self.setregion(head, tail, chars, lines)
|
self.set_region(head, tail, chars, lines)
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
def commentregion(self, event):
|
def comment_region_event(self, event):
|
||||||
head, tail, chars, lines = self.getregion()
|
head, tail, chars, lines = self.get_region()
|
||||||
for pos in range(len(lines)):
|
for pos in range(len(lines)):
|
||||||
line = lines[pos]
|
line = lines[pos]
|
||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
lines[pos] = '##' + line
|
lines[pos] = '##' + line
|
||||||
self.setregion(head, tail, chars, lines)
|
self.set_region(head, tail, chars, lines)
|
||||||
|
|
||||||
def uncommentregion(self, event):
|
def uncomment_region_event(self, event):
|
||||||
head, tail, chars, lines = self.getregion()
|
head, tail, chars, lines = self.get_region()
|
||||||
for pos in range(len(lines)):
|
for pos in range(len(lines)):
|
||||||
line = lines[pos]
|
line = lines[pos]
|
||||||
if not line:
|
if not line:
|
||||||
|
@ -98,9 +173,19 @@ class AutoIndent:
|
||||||
elif line[:1] == '#':
|
elif line[:1] == '#':
|
||||||
line = line[1:]
|
line = line[1:]
|
||||||
lines[pos] = line
|
lines[pos] = line
|
||||||
self.setregion(head, tail, chars, lines)
|
self.set_region(head, tail, chars, lines)
|
||||||
|
|
||||||
def getregion(self):
|
def tabify_region_event(self, event):
|
||||||
|
head, tail, chars, lines = self.get_region()
|
||||||
|
lines = map(tabify, lines)
|
||||||
|
self.set_region(head, tail, chars, lines)
|
||||||
|
|
||||||
|
def untabify_region_event(self, event):
|
||||||
|
head, tail, chars, lines = self.get_region()
|
||||||
|
lines = map(string.expandtabs, lines)
|
||||||
|
self.set_region(head, tail, chars, lines)
|
||||||
|
|
||||||
|
def get_region(self):
|
||||||
text = self.text
|
text = self.text
|
||||||
head = text.index("sel.first linestart")
|
head = text.index("sel.first linestart")
|
||||||
tail = text.index("sel.last -1c lineend +1c")
|
tail = text.index("sel.last -1c lineend +1c")
|
||||||
|
@ -111,7 +196,7 @@ class AutoIndent:
|
||||||
lines = string.split(chars, "\n")
|
lines = string.split(chars, "\n")
|
||||||
return head, tail, chars, lines
|
return head, tail, chars, lines
|
||||||
|
|
||||||
def setregion(self, head, tail, chars, lines):
|
def set_region(self, head, tail, chars, lines):
|
||||||
text = self.text
|
text = self.text
|
||||||
newchars = string.join(lines, "\n")
|
newchars = string.join(lines, "\n")
|
||||||
if newchars == chars:
|
if newchars == chars:
|
||||||
|
@ -122,3 +207,12 @@ class AutoIndent:
|
||||||
text.delete(head, tail)
|
text.delete(head, tail)
|
||||||
text.insert(head, newchars)
|
text.insert(head, newchars)
|
||||||
text.tag_add("sel", head, "insert")
|
text.tag_add("sel", head, "insert")
|
||||||
|
|
||||||
|
def tabify(line, tabsize=8):
|
||||||
|
spaces = tabsize * ' '
|
||||||
|
for i in range(0, len(line), tabsize):
|
||||||
|
if line[i:i+tabsize] != spaces:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
i = len(line)
|
||||||
|
return '\t' * (i/tabsize) + line[i:]
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import sys
|
import sys
|
||||||
import string
|
import string
|
||||||
import re
|
import re
|
||||||
|
from keydefs import *
|
||||||
|
|
||||||
menudefs = [
|
menudefs = [
|
||||||
# underscore prefixes character to underscore
|
# underscore prefixes character to underscore
|
||||||
|
@ -15,7 +16,8 @@ menudefs = [
|
||||||
('_New window', '<<open-new-window>>'),
|
('_New window', '<<open-new-window>>'),
|
||||||
('_Open...', '<<open-window-from-file>>'),
|
('_Open...', '<<open-window-from-file>>'),
|
||||||
('Open _module...', '<<open-module>>'),
|
('Open _module...', '<<open-module>>'),
|
||||||
('Class _browser...', '<<open-class-browser>>'),
|
('Class _browser', '<<open-class-browser>>'),
|
||||||
|
('Python shell', '<<open-python-shell>>'),
|
||||||
None,
|
None,
|
||||||
('_Save', '<<save-window>>'),
|
('_Save', '<<save-window>>'),
|
||||||
('Save _As...', '<<save-window-as-file>>'),
|
('Save _As...', '<<save-window-as-file>>'),
|
||||||
|
@ -31,19 +33,15 @@ menudefs = [
|
||||||
('Cu_t', '<<Cut>>'),
|
('Cu_t', '<<Cut>>'),
|
||||||
('_Copy', '<<Copy>>'),
|
('_Copy', '<<Copy>>'),
|
||||||
('_Paste', '<<Paste>>'),
|
('_Paste', '<<Paste>>'),
|
||||||
None,
|
('Select _All', '<<select-all>>'),
|
||||||
('_Find...', '<<find>>'),
|
]),
|
||||||
('Find _next', '<<find-next>>'),
|
('script', [
|
||||||
('Find _same', '<<find-same>>'),
|
('Run module', '<<run-module>>'),
|
||||||
('_Go to line', '<<goto-line>>'),
|
('Run script', '<<run-script>>'),
|
||||||
None,
|
('New shell', '<<new-shell>>'),
|
||||||
('_Dedent region', '<<dedent-region>>'),
|
|
||||||
('_Indent region', '<<indent-region>>'),
|
|
||||||
('Comment _out region', '<<comment-region>>'),
|
|
||||||
('U_ncomment region', '<<uncomment-region>>'),
|
|
||||||
]),
|
]),
|
||||||
('debug', [
|
('debug', [
|
||||||
('_Go to line from traceback', '<<goto-traceback-line>>'),
|
('_Go to file/line', '<<goto-file-line>>'),
|
||||||
('_Open stack viewer', '<<open-stack-viewer>>'),
|
('_Open stack viewer', '<<open-stack-viewer>>'),
|
||||||
('_Debugger toggle', '<<toggle-debugger>>'),
|
('_Debugger toggle', '<<toggle-debugger>>'),
|
||||||
]),
|
]),
|
||||||
|
@ -54,81 +52,6 @@ menudefs = [
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
|
|
||||||
windows_keydefs = {
|
|
||||||
'<<beginning-of-line>>': ['<Control-a>', '<Home>'],
|
|
||||||
'<<close-all-windows>>': ['<Control-q>'],
|
|
||||||
'<<comment-region>>': ['<Meta-Key-3>', '<Alt-Key-3>'],
|
|
||||||
'<<dedent-region>>': ['<Control-bracketleft>'],
|
|
||||||
'<<dump-undo-state>>': ['<Control-backslash>'],
|
|
||||||
'<<end-of-file>>': ['<Control-d>'],
|
|
||||||
'<<expand-word>>': ['<Meta-slash>', '<Alt-slash>'],
|
|
||||||
'<<find-next>>': ['<F3>', '<Control-g>'],
|
|
||||||
'<<find-same>>': ['<Control-F3>'],
|
|
||||||
'<<find>>': ['<Control-f>'],
|
|
||||||
'<<goto-line>>': ['<Alt-g>', '<Meta-g>'],
|
|
||||||
'<<history-next>>': ['<Meta-n>', '<Alt-n>'],
|
|
||||||
'<<history-previous>>': ['<Meta-p>', '<Alt-p>'],
|
|
||||||
'<<indent-region>>': ['<Control-bracketright>'],
|
|
||||||
'<<interrupt-execution>>': ['<Control-c>'],
|
|
||||||
'<<newline-and-indent>>': ['<Key-Return>', '<KP_Enter>'],
|
|
||||||
'<<open-new-window>>': ['<Control-n>'],
|
|
||||||
'<<open-window-from-file>>': ['<Control-o>'],
|
|
||||||
'<<plain-newline-and-indent>>': ['<Control-j>'],
|
|
||||||
'<<redo>>': ['<Control-y>'],
|
|
||||||
'<<save-copy-of-window-as-file>>': ['<Meta-w>'],
|
|
||||||
'<<save-window-as-file>>': ['<Control-w>'],
|
|
||||||
'<<save-window>>': ['<Control-s>'],
|
|
||||||
'<<toggle-auto-coloring>>': ['<Control-slash>'],
|
|
||||||
'<<uncomment-region>>': ['<Meta-Key-4>', '<Alt-Key-4>'],
|
|
||||||
'<<undo>>': ['<Control-z>'],
|
|
||||||
}
|
|
||||||
|
|
||||||
emacs_keydefs = {
|
|
||||||
'<<Copy>>': ['<Alt-w>'],
|
|
||||||
'<<Cut>>': ['<Control-w>'],
|
|
||||||
'<<Paste>>': ['<Control-y>'],
|
|
||||||
'<<about-idle>>': [],
|
|
||||||
'<<beginning-of-line>>': ['<Control-a>', '<Home>'],
|
|
||||||
'<<center-insert>>': ['<Control-l>'],
|
|
||||||
'<<close-all-windows>>': ['<Control-x><Control-c>'],
|
|
||||||
'<<close-window>>': ['<Control-x><Control-0>'],
|
|
||||||
'<<comment-region>>': ['<Meta-Key-3>', '<Alt-Key-3>'],
|
|
||||||
'<<dedent-region>>': ['<Meta-bracketleft>',
|
|
||||||
'<Alt-bracketleft>',
|
|
||||||
'<Control-bracketleft>'],
|
|
||||||
'<<do-nothing>>': ['<Control-x>'],
|
|
||||||
'<<dump-undo-state>>': ['<Control-backslash>'],
|
|
||||||
'<<end-of-file>>': ['<Control-d>'],
|
|
||||||
'<<expand-word>>': ['<Meta-slash>', '<Alt-slash>'],
|
|
||||||
'<<find-next>>': ['<Control-u><Control-s>'],
|
|
||||||
'<<find-same>>': ['<Control-s>'],
|
|
||||||
'<<find>>': ['<Control-u><Control-u><Control-s>'],
|
|
||||||
'<<goto-line>>': ['<Alt-g>', '<Meta-g>'],
|
|
||||||
'<<goto-traceback-line>>': [],
|
|
||||||
'<<help>>': [],
|
|
||||||
'<<history-next>>': ['<Meta-n>', '<Alt-n>'],
|
|
||||||
'<<history-previous>>': ['<Meta-p>', '<Alt-p>'],
|
|
||||||
'<<indent-region>>': ['<Meta-bracketright>',
|
|
||||||
'<Alt-bracketright>',
|
|
||||||
'<Control-bracketright>'],
|
|
||||||
'<<interrupt-execution>>': ['<Control-c>'],
|
|
||||||
'<<newline-and-indent>>': ['<Key-Return>', '<KP_Enter>'],
|
|
||||||
'<<open-class-browser>>': ['<Control-x><Control-b>'],
|
|
||||||
'<<open-module>>': ['<Control-x><Control-m>'],
|
|
||||||
'<<open-new-window>>': ['<Control-x><Control-n>'],
|
|
||||||
'<<open-stack-viewer>>': [],
|
|
||||||
'<<open-window-from-file>>': ['<Control-x><Control-f>'],
|
|
||||||
'<<plain-newline-and-indent>>': ['<Control-j>'],
|
|
||||||
'<<redo>>': ['<Alt-z>', '<Meta-z>'],
|
|
||||||
'<<save-copy-of-window-as-file>>': ['<Control-x><w>'],
|
|
||||||
'<<save-window-as-file>>': ['<Control-x><Control-w>'],
|
|
||||||
'<<save-window>>': ['<Control-x><Control-s>'],
|
|
||||||
'<<toggle-auto-coloring>>': ['<Control-slash>'],
|
|
||||||
'<<toggle-debugger>>': [],
|
|
||||||
'<<uncomment-region>>': ['<Meta-Key-4>', '<Alt-Key-4>'],
|
|
||||||
'<<undo>>': ['<Control-z>'],
|
|
||||||
}
|
|
||||||
|
|
||||||
def prepstr(s):
|
def prepstr(s):
|
||||||
# Helper to extract the underscore from a string,
|
# Helper to extract the underscore from a string,
|
||||||
# e.g. prepstr("Co_py") returns (2, "Copy").
|
# e.g. prepstr("Co_py") returns (2, "Copy").
|
||||||
|
@ -140,18 +63,14 @@ def prepstr(s):
|
||||||
keynames = {
|
keynames = {
|
||||||
'bracketleft': '[',
|
'bracketleft': '[',
|
||||||
'bracketright': ']',
|
'bracketright': ']',
|
||||||
|
'slash': '/',
|
||||||
}
|
}
|
||||||
|
|
||||||
def getaccelerator(keydefs, event):
|
def get_accelerator(keydefs, event):
|
||||||
keylist = keydefs.get(event)
|
keylist = keydefs.get(event)
|
||||||
if not keylist:
|
if not keylist:
|
||||||
return ""
|
return ""
|
||||||
s = keylist[0]
|
s = keylist[0]
|
||||||
if s[:6] == "<Meta-":
|
|
||||||
# Prefer Alt over Meta -- they should be the same thing anyway
|
|
||||||
alts = "<Alt-" + s[6:]
|
|
||||||
if alts in keylist:
|
|
||||||
s = alts
|
|
||||||
s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s)
|
s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s)
|
||||||
s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
|
s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
|
||||||
s = re.sub("Key-", "", s)
|
s = re.sub("Key-", "", s)
|
||||||
|
@ -165,7 +84,7 @@ def getaccelerator(keydefs, event):
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
default_keydefs = windows_keydefs
|
default_keydefs = windows_keydefs
|
||||||
else:
|
else:
|
||||||
default_keydefs = emacs_keydefs
|
default_keydefs = unix_keydefs
|
||||||
|
|
||||||
def apply_bindings(text, keydefs=default_keydefs):
|
def apply_bindings(text, keydefs=default_keydefs):
|
||||||
text.keydefs = keydefs
|
text.keydefs = keydefs
|
||||||
|
@ -173,14 +92,10 @@ def apply_bindings(text, keydefs=default_keydefs):
|
||||||
if keylist:
|
if keylist:
|
||||||
apply(text.event_add, (event,) + tuple(keylist))
|
apply(text.event_add, (event,) + tuple(keylist))
|
||||||
|
|
||||||
def fill_menus(text, menudict, defs=menudefs):
|
def fill_menus(text, menudict, defs=menudefs, keydefs=default_keydefs):
|
||||||
# Fill the menus for the given text widget. The menudict argument is
|
# Fill the menus for the given text widget. The menudict argument is
|
||||||
# a dictionary containing the menus, keyed by their lowercased name.
|
# a dictionary containing the menus, keyed by their lowercased name.
|
||||||
# Menus that are absent or None are ignored.
|
# Menus that are absent or None are ignored.
|
||||||
if hasattr(text, "keydefs"):
|
|
||||||
keydefs = text.keydefs
|
|
||||||
else:
|
|
||||||
keydefs = default_keydefs
|
|
||||||
for mname, itemlist in defs:
|
for mname, itemlist in defs:
|
||||||
menu = menudict.get(mname)
|
menu = menudict.get(mname)
|
||||||
if not menu:
|
if not menu:
|
||||||
|
@ -191,7 +106,7 @@ def fill_menus(text, menudict, defs=menudefs):
|
||||||
else:
|
else:
|
||||||
label, event = item
|
label, event = item
|
||||||
underline, label = prepstr(label)
|
underline, label = prepstr(label)
|
||||||
accelerator = getaccelerator(keydefs, event)
|
accelerator = get_accelerator(keydefs, event)
|
||||||
def command(text=text, event=event):
|
def command(text=text, event=event):
|
||||||
text.event_generate(event)
|
text.event_generate(event)
|
||||||
menu.add_command(label=label, underline=underline,
|
menu.add_command(label=label, underline=underline,
|
||||||
|
|
|
@ -14,6 +14,7 @@ import string
|
||||||
import pyclbr
|
import pyclbr
|
||||||
from Tkinter import *
|
from Tkinter import *
|
||||||
import tkMessageBox
|
import tkMessageBox
|
||||||
|
from WindowList import ListedToplevel
|
||||||
|
|
||||||
from ScrolledList import ScrolledList
|
from ScrolledList import ScrolledList
|
||||||
|
|
||||||
|
@ -34,9 +35,10 @@ class ClassBrowser:
|
||||||
self.flist = flist
|
self.flist = flist
|
||||||
self.dict = dict
|
self.dict = dict
|
||||||
self.root = root
|
self.root = root
|
||||||
self.top = top = Toplevel(root)
|
self.top = top = ListedToplevel(root)
|
||||||
self.top.protocol("WM_DELETE_WINDOW", self.close)
|
self.top.protocol("WM_DELETE_WINDOW", self.close)
|
||||||
top.wm_title("Class browser")
|
top.wm_title("Class Browser - " + name)
|
||||||
|
top.wm_iconname("ClBrowser")
|
||||||
self.leftframe = leftframe = Frame(top)
|
self.leftframe = leftframe = Frame(top)
|
||||||
self.leftframe.pack(side="left", fill="both", expand=1)
|
self.leftframe.pack(side="left", fill="both", expand=1)
|
||||||
# Create help label
|
# Create help label
|
||||||
|
|
|
@ -5,6 +5,10 @@ import keyword
|
||||||
from Tkinter import *
|
from Tkinter import *
|
||||||
from Delegator import Delegator
|
from Delegator import Delegator
|
||||||
|
|
||||||
|
#$ event <<toggle-auto-coloring>>
|
||||||
|
#$ win <Control-slash>
|
||||||
|
#$ unix <Control-slash>
|
||||||
|
|
||||||
__debug__ = 0
|
__debug__ = 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,6 +60,9 @@ class ColorDelegator(Delegator):
|
||||||
"TODO": {}, #{"background": "#cccccc"},
|
"TODO": {}, #{"background": "#cccccc"},
|
||||||
|
|
||||||
"BREAK": {"background": "#FF7777"},
|
"BREAK": {"background": "#FF7777"},
|
||||||
|
|
||||||
|
# The following is used by ReplaceDialog:
|
||||||
|
"hit": {"foreground": "#FFFFFF", "background": "#000000"},
|
||||||
}
|
}
|
||||||
|
|
||||||
def insert(self, index, chars, tags=None):
|
def insert(self, index, chars, tags=None):
|
||||||
|
|
|
@ -2,6 +2,7 @@ import os
|
||||||
import bdb
|
import bdb
|
||||||
import traceback
|
import traceback
|
||||||
from Tkinter import *
|
from Tkinter import *
|
||||||
|
from WindowList import ListedToplevel
|
||||||
|
|
||||||
import StackViewer
|
import StackViewer
|
||||||
|
|
||||||
|
@ -46,7 +47,9 @@ class Debugger(bdb.Bdb):
|
||||||
pyshell = self.pyshell
|
pyshell = self.pyshell
|
||||||
self.flist = pyshell.flist
|
self.flist = pyshell.flist
|
||||||
self.root = root = pyshell.root
|
self.root = root = pyshell.root
|
||||||
self.top = top = Toplevel(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)
|
top.wm_protocol("WM_DELETE_WINDOW", self.close)
|
||||||
#
|
#
|
||||||
self.bframe = bframe = Frame(top)
|
self.bframe = bframe = Frame(top)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
class Delegator:
|
class Delegator:
|
||||||
|
|
||||||
# The cache is only used to be able to change delegates!
|
# The cache is only used to be able to change delegates!
|
||||||
|
|
|
@ -5,15 +5,70 @@ import imp
|
||||||
from Tkinter import *
|
from Tkinter import *
|
||||||
import tkSimpleDialog
|
import tkSimpleDialog
|
||||||
import tkMessageBox
|
import tkMessageBox
|
||||||
|
import idlever
|
||||||
|
|
||||||
|
# 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 <<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_title = "About IDLE"
|
||||||
about_text = """\
|
about_text = """\
|
||||||
IDLE 0.1
|
IDLE %s
|
||||||
|
|
||||||
A not totally unintegrated development environment for Python
|
An Integrated DeveLopment Environment for Python
|
||||||
|
|
||||||
by Guido van Rossum
|
by Guido van Rossum
|
||||||
"""
|
""" % idlever.IDLE_VERSION
|
||||||
|
|
||||||
class EditorWindow:
|
class EditorWindow:
|
||||||
|
|
||||||
|
@ -21,44 +76,52 @@ class EditorWindow:
|
||||||
from ColorDelegator import ColorDelegator
|
from ColorDelegator import ColorDelegator
|
||||||
from UndoDelegator import UndoDelegator
|
from UndoDelegator import UndoDelegator
|
||||||
from IOBinding import IOBinding
|
from IOBinding import IOBinding
|
||||||
from SearchBinding import SearchBinding
|
|
||||||
from AutoIndent import AutoIndent
|
|
||||||
from AutoExpand import AutoExpand
|
|
||||||
import Bindings
|
import Bindings
|
||||||
|
from Tkinter import Toplevel
|
||||||
|
|
||||||
about_title = about_title
|
about_title = about_title
|
||||||
about_text = about_text
|
about_text = about_text
|
||||||
|
|
||||||
def __init__(self, root, filename=None):
|
def __init__(self, flist=None, filename=None, key=None, root=None):
|
||||||
|
self.flist = flist
|
||||||
|
root = root or flist.root
|
||||||
self.root = root
|
self.root = root
|
||||||
self.menubar = Menu(root)
|
self.menubar = Menu(root)
|
||||||
self.top = top = Toplevel(root, menu=self.menubar)
|
self.top = top = self.Toplevel(root, menu=self.menubar)
|
||||||
self.vbar = vbar = Scrollbar(top, name='vbar')
|
self.vbar = vbar = Scrollbar(top, name='vbar')
|
||||||
self.text = text = Text(top, name='text')
|
self.text = text = Text(top, name='text', padx=5,
|
||||||
|
background="white", wrap="none")
|
||||||
|
|
||||||
self.createmenubar()
|
self.createmenubar()
|
||||||
self.Bindings.apply_bindings(text)
|
self.Bindings.apply_bindings(text)
|
||||||
|
|
||||||
self.top.protocol("WM_DELETE_WINDOW", self.close)
|
self.top.protocol("WM_DELETE_WINDOW", self.close)
|
||||||
self.top.bind("<<close-window>>", self.close_event)
|
self.top.bind("<<close-window>>", self.close_event)
|
||||||
self.text.bind("<<center-insert>>", self.center_insert_event)
|
text.bind("<<center-insert>>", self.center_insert_event)
|
||||||
self.text.bind("<<help>>", self.help_dialog)
|
text.bind("<<help>>", self.help_dialog)
|
||||||
self.text.bind("<<about-idle>>", self.about_dialog)
|
text.bind("<<about-idle>>", self.about_dialog)
|
||||||
self.text.bind("<<open-module>>", self.open_module)
|
text.bind("<<open-module>>", self.open_module)
|
||||||
self.text.bind("<<do-nothing>>", lambda event: "break")
|
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)
|
||||||
|
|
||||||
vbar['command'] = text.yview
|
vbar['command'] = text.yview
|
||||||
vbar.pack(side=RIGHT, fill=Y)
|
vbar.pack(side=RIGHT, fill=Y)
|
||||||
|
|
||||||
text['yscrollcommand'] = vbar.set
|
text['yscrollcommand'] = vbar.set
|
||||||
text['background'] = 'white'
|
|
||||||
if sys.platform[:3] == 'win':
|
if sys.platform[:3] == 'win':
|
||||||
text['font'] = ("lucida console", 8)
|
text['font'] = ("lucida console", 8)
|
||||||
text.pack(side=LEFT, fill=BOTH, expand=1)
|
text.pack(side=LEFT, fill=BOTH, expand=1)
|
||||||
text.focus_set()
|
text.focus_set()
|
||||||
|
|
||||||
self.auto = auto = self.AutoIndent(text)
|
|
||||||
self.autoex = self.AutoExpand(text)
|
|
||||||
self.per = per = self.Percolator(text)
|
self.per = per = self.Percolator(text)
|
||||||
if self.ispythonsource(filename):
|
if self.ispythonsource(filename):
|
||||||
self.color = color = self.ColorDelegator(); per.insertfilter(color)
|
self.color = color = self.ColorDelegator(); per.insertfilter(color)
|
||||||
|
@ -67,8 +130,7 @@ class EditorWindow:
|
||||||
##print "No initial colorizer"
|
##print "No initial colorizer"
|
||||||
self.color = None
|
self.color = None
|
||||||
self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
|
self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
|
||||||
self.search = search = self.SearchBinding(undo)
|
self.io = io = self.IOBinding(self)
|
||||||
self.io = io = self.IOBinding(undo)
|
|
||||||
|
|
||||||
undo.set_saved_change_hook(self.saved_change_hook)
|
undo.set_saved_change_hook(self.saved_change_hook)
|
||||||
io.set_filename_change_hook(self.filename_change_hook)
|
io.set_filename_change_hook(self.filename_change_hook)
|
||||||
|
@ -81,9 +143,29 @@ class EditorWindow:
|
||||||
|
|
||||||
self.saved_change_hook()
|
self.saved_change_hook()
|
||||||
|
|
||||||
|
self.load_extensions()
|
||||||
|
|
||||||
|
menu = self.menudict.get('windows')
|
||||||
|
if menu:
|
||||||
|
menu.configure(tearoff=0)
|
||||||
|
end = menu.index("end")
|
||||||
|
if end is None:
|
||||||
|
end = -1
|
||||||
|
if end >= 0:
|
||||||
|
menu.add_separator()
|
||||||
|
end = end + 1
|
||||||
|
self.wmenu_end = end
|
||||||
|
menu.configure(postcommand=self.postwindowsmenu)
|
||||||
|
|
||||||
|
def wakeup(self):
|
||||||
|
self.top.tkraise()
|
||||||
|
self.top.wm_deiconify()
|
||||||
|
self.text.focus_set()
|
||||||
|
|
||||||
menu_specs = [
|
menu_specs = [
|
||||||
("file", "_File"),
|
("file", "_File"),
|
||||||
("edit", "_Edit"),
|
("edit", "_Edit"),
|
||||||
|
("windows", "_Windows"),
|
||||||
("help", "_Help"),
|
("help", "_Help"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -96,15 +178,78 @@ class EditorWindow:
|
||||||
mbar.add_cascade(label=label, menu=menu, underline=underline)
|
mbar.add_cascade(label=label, menu=menu, underline=underline)
|
||||||
self.Bindings.fill_menus(self.text, mdict)
|
self.Bindings.fill_menus(self.text, mdict)
|
||||||
|
|
||||||
|
def postwindowsmenu(self):
|
||||||
|
# Only called when Windows menu exists
|
||||||
|
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)
|
||||||
|
import WindowList
|
||||||
|
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):
|
def about_dialog(self, event=None):
|
||||||
tkMessageBox.showinfo(self.about_title, self.about_text,
|
tkMessageBox.showinfo(self.about_title, self.about_text,
|
||||||
master=self.text)
|
master=self.text)
|
||||||
|
|
||||||
|
helpfile = "help.txt"
|
||||||
|
|
||||||
def help_dialog(self, event=None):
|
def help_dialog(self, event=None):
|
||||||
from HelpWindow import HelpWindow
|
helpfile = self.helpfile
|
||||||
HelpWindow(root=self.root)
|
if not os.path.exists(helpfile):
|
||||||
|
base = os.path.basename(self.helpfile)
|
||||||
|
for dir in sys.path:
|
||||||
|
fullname = os.path.join(dir, base)
|
||||||
|
if os.path.exists(fullname):
|
||||||
|
helpfile = fullname
|
||||||
|
break
|
||||||
|
if self.flist:
|
||||||
|
self.flist.open(helpfile)
|
||||||
|
else:
|
||||||
|
self.io.loadfile(helpfile)
|
||||||
|
|
||||||
|
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):
|
def open_module(self, event=None):
|
||||||
|
# XXX Shouldn't this be in IOBinding or in FileList?
|
||||||
try:
|
try:
|
||||||
name = self.text.get("sel.first", "sel.last")
|
name = self.text.get("sel.first", "sel.last")
|
||||||
except TclError:
|
except TclError:
|
||||||
|
@ -120,6 +265,8 @@ class EditorWindow:
|
||||||
name = string.strip(name)
|
name = string.strip(name)
|
||||||
if not name:
|
if not name:
|
||||||
return
|
return
|
||||||
|
# XXX Ought to support package syntax
|
||||||
|
# XXX Ought to insert current file's directory in front of path
|
||||||
try:
|
try:
|
||||||
(f, file, (suffix, mode, type)) = imp.find_module(name)
|
(f, file, (suffix, mode, type)) = imp.find_module(name)
|
||||||
except ImportError, msg:
|
except ImportError, msg:
|
||||||
|
@ -131,7 +278,26 @@ class EditorWindow:
|
||||||
return
|
return
|
||||||
if f:
|
if f:
|
||||||
f.close()
|
f.close()
|
||||||
self.flist.open(file, self)
|
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)
|
||||||
|
return None
|
||||||
|
head, tail = os.path.split(filename)
|
||||||
|
base, ext = os.path.splitext(tail)
|
||||||
|
import pyclbr
|
||||||
|
if pyclbr._modules.has_key(base):
|
||||||
|
del pyclbr._modules[base]
|
||||||
|
import ClassBrowser
|
||||||
|
ClassBrowser.ClassBrowser(self.flist, base, [head])
|
||||||
|
|
||||||
def gotoline(self, lineno):
|
def gotoline(self, lineno):
|
||||||
if lineno is not None and lineno > 0:
|
if lineno is not None and lineno > 0:
|
||||||
|
@ -143,7 +309,8 @@ class EditorWindow:
|
||||||
def ispythonsource(self, filename):
|
def ispythonsource(self, filename):
|
||||||
if not filename:
|
if not filename:
|
||||||
return 1
|
return 1
|
||||||
if os.path.normcase(filename[-3:]) == ".py":
|
base, ext = os.path.splitext(os.path.basename(filename))
|
||||||
|
if os.path.normcase(ext) in (".py", ".pyw"):
|
||||||
return 1
|
return 1
|
||||||
try:
|
try:
|
||||||
f = open(filename)
|
f = open(filename)
|
||||||
|
@ -153,12 +320,16 @@ class EditorWindow:
|
||||||
return 0
|
return 0
|
||||||
return line[:2] == '#!' and string.find(line, 'python') >= 0
|
return line[:2] == '#!' and string.find(line, 'python') >= 0
|
||||||
|
|
||||||
close_hook = None
|
def close_hook(self):
|
||||||
|
if self.flist:
|
||||||
|
self.flist.close_edit(self)
|
||||||
|
|
||||||
def set_close_hook(self, close_hook):
|
def set_close_hook(self, close_hook):
|
||||||
self.close_hook = close_hook
|
self.close_hook = close_hook
|
||||||
|
|
||||||
def filename_change_hook(self):
|
def filename_change_hook(self):
|
||||||
|
if self.flist:
|
||||||
|
self.flist.filename_changed_edit(self)
|
||||||
self.saved_change_hook()
|
self.saved_change_hook()
|
||||||
if self.ispythonsource(self.io.filename):
|
if self.ispythonsource(self.io.filename):
|
||||||
self.addcolorizer()
|
self.addcolorizer()
|
||||||
|
@ -184,13 +355,40 @@ class EditorWindow:
|
||||||
self.per.insertfilter(self.undo)
|
self.per.insertfilter(self.undo)
|
||||||
|
|
||||||
def saved_change_hook(self):
|
def saved_change_hook(self):
|
||||||
if self.io.filename:
|
short = self.short_title()
|
||||||
title = self.io.filename
|
long = self.long_title()
|
||||||
|
if short and long:
|
||||||
|
title = short + " - " + long
|
||||||
|
elif short:
|
||||||
|
title = short
|
||||||
|
elif long:
|
||||||
|
title = long
|
||||||
else:
|
else:
|
||||||
title = "(Untitled)"
|
title = "Untitled"
|
||||||
if not self.undo.get_saved():
|
icon = short or long or title
|
||||||
title = title + " *"
|
if not self.get_saved():
|
||||||
|
title = "*%s*" % title
|
||||||
|
icon = "*%s" % icon
|
||||||
self.top.wm_title(title)
|
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):
|
def center_insert_event(self, event):
|
||||||
self.center()
|
self.center()
|
||||||
|
@ -207,10 +405,14 @@ class EditorWindow:
|
||||||
def close_event(self, event):
|
def close_event(self, event):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
def maybesave(self):
|
||||||
|
if self.io:
|
||||||
|
return self.io.maybesave()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.top.wm_deiconify()
|
self.top.wm_deiconify()
|
||||||
self.top.tkraise()
|
self.top.tkraise()
|
||||||
reply = self.io.maybesave()
|
reply = self.maybesave()
|
||||||
if reply != "cancel":
|
if reply != "cancel":
|
||||||
if self.color and self.color.colorizing:
|
if self.color and self.color.colorizing:
|
||||||
self.color.close()
|
self.color.close()
|
||||||
|
@ -223,8 +425,59 @@ class EditorWindow:
|
||||||
self.top.destroy()
|
self.top.destroy()
|
||||||
return reply
|
return reply
|
||||||
|
|
||||||
|
def load_extensions(self):
|
||||||
|
self.extensions = {}
|
||||||
|
self.load_standard_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):
|
||||||
|
import extend
|
||||||
|
return extend.standard
|
||||||
|
|
||||||
|
def load_extension(self, name):
|
||||||
|
mod = __import__(name)
|
||||||
|
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.Bindings.apply_bindings(self.text, keydefs)
|
||||||
|
for vevent in keydefs.keys():
|
||||||
|
methodname = string.replace(vevent, "-", "_")
|
||||||
|
while methodname[:1] == '<':
|
||||||
|
methodname = methodname[1:]
|
||||||
|
while methodname[-1:] == '>':
|
||||||
|
methodname = methodname[:-1]
|
||||||
|
methodname = methodname + "_event"
|
||||||
|
if hasattr(ins, methodname):
|
||||||
|
self.text.bind(vevent, getattr(ins, methodname))
|
||||||
|
if hasattr(ins, "menudefs"):
|
||||||
|
self.Bindings.fill_menus(self.text, self. menudict,
|
||||||
|
ins.menudefs, keydefs)
|
||||||
|
return ins
|
||||||
|
|
||||||
|
|
||||||
def fixwordbreaks(root):
|
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 = root.tk
|
||||||
tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
|
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_wordchars', '[a-zA-Z0-9_]')
|
||||||
|
@ -239,7 +492,7 @@ def test():
|
||||||
filename = sys.argv[1]
|
filename = sys.argv[1]
|
||||||
else:
|
else:
|
||||||
filename = None
|
filename = None
|
||||||
edit = EditorWindow(root, filename)
|
edit = EditorWindow(root=root, filename=filename)
|
||||||
edit.set_close_hook(root.quit)
|
edit.set_close_hook(root.quit)
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
root.destroy()
|
root.destroy()
|
||||||
|
|
|
@ -2,97 +2,38 @@ import os
|
||||||
from Tkinter import *
|
from Tkinter import *
|
||||||
import tkMessageBox
|
import tkMessageBox
|
||||||
|
|
||||||
from EditorWindow import EditorWindow, fixwordbreaks
|
import WindowList
|
||||||
from IOBinding import IOBinding
|
|
||||||
|
|
||||||
|
#$ event <<open-new-window>>
|
||||||
|
#$ win <Control-n>
|
||||||
|
#$ unix <Control-x><Control-n>
|
||||||
|
|
||||||
class MultiIOBinding(IOBinding):
|
# (This is labeled as 'Exit'in the File menu)
|
||||||
|
#$ event <<close-all-windows>>
|
||||||
def open(self, event):
|
#$ win <Control-q>
|
||||||
filename = self.askopenfile()
|
#$ unix <Control-x><Control-c>
|
||||||
if filename:
|
|
||||||
self.flist.open(filename, self.edit)
|
|
||||||
return "break"
|
|
||||||
|
|
||||||
|
|
||||||
class MultiEditorWindow(EditorWindow):
|
|
||||||
|
|
||||||
IOBinding = MultiIOBinding
|
|
||||||
|
|
||||||
# Override menu bar specs
|
|
||||||
menu_specs = EditorWindow.menu_specs[:]
|
|
||||||
menu_specs.insert(len(menu_specs)-1, ("windows", "_Windows"))
|
|
||||||
|
|
||||||
def __init__(self, flist, filename, key):
|
|
||||||
self.flist = flist
|
|
||||||
flist.inversedict[self] = key
|
|
||||||
if key:
|
|
||||||
flist.dict[key] = self
|
|
||||||
EditorWindow.__init__(self, flist.root, filename)
|
|
||||||
self.io.flist = flist
|
|
||||||
self.io.edit = self
|
|
||||||
self.text.bind("<<open-new-window>>", self.flist.new_callback)
|
|
||||||
self.text.bind("<<close-all-windows>>", self.flist.close_all_callback)
|
|
||||||
self.text.bind("<<open-class-browser>>", self.open_class_browser)
|
|
||||||
|
|
||||||
def close_hook(self):
|
|
||||||
self.flist.close_edit(self)
|
|
||||||
|
|
||||||
def filename_change_hook(self):
|
|
||||||
self.flist.filename_changed_edit(self)
|
|
||||||
EditorWindow.filename_change_hook(self)
|
|
||||||
|
|
||||||
def wakeup(self):
|
|
||||||
self.top.tkraise()
|
|
||||||
self.top.wm_deiconify()
|
|
||||||
self.text.focus_set()
|
|
||||||
|
|
||||||
def createmenubar(self):
|
|
||||||
EditorWindow.createmenubar(self)
|
|
||||||
self.menudict['windows'].configure(postcommand=self.postwindowsmenu)
|
|
||||||
|
|
||||||
def postwindowsmenu(self):
|
|
||||||
wmenu = self.menudict['windows']
|
|
||||||
wmenu.delete(0, 'end')
|
|
||||||
self.fixedwindowsmenu(wmenu)
|
|
||||||
files = self.flist.dict.keys()
|
|
||||||
files.sort()
|
|
||||||
for file in files:
|
|
||||||
def openit(self=self, file=file):
|
|
||||||
self.flist.open(file)
|
|
||||||
wmenu.add_command(label=file, command=openit)
|
|
||||||
|
|
||||||
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)
|
|
||||||
return None
|
|
||||||
head, tail = os.path.split(filename)
|
|
||||||
base, ext = os.path.splitext(tail)
|
|
||||||
import pyclbr
|
|
||||||
if pyclbr._modules.has_key(base):
|
|
||||||
del pyclbr._modules[base]
|
|
||||||
import ClassBrowser
|
|
||||||
ClassBrowser.ClassBrowser(self.flist, base, [head])
|
|
||||||
|
|
||||||
|
|
||||||
class FileList:
|
class FileList:
|
||||||
|
|
||||||
EditorWindow = MultiEditorWindow
|
from EditorWindow import EditorWindow
|
||||||
|
EditorWindow.Toplevel = WindowList.ListedToplevel # XXX Patch it!
|
||||||
|
|
||||||
def __init__(self, root):
|
def __init__(self, root):
|
||||||
self.root = root
|
self.root = root
|
||||||
self.dict = {}
|
self.dict = {}
|
||||||
self.inversedict = {}
|
self.inversedict = {}
|
||||||
|
|
||||||
def new(self):
|
|
||||||
return self.open(None)
|
|
||||||
|
|
||||||
def open(self, filename, edit=None):
|
def goodname(self, filename):
|
||||||
if filename:
|
filename = self.canonize(filename)
|
||||||
|
key = os.path.normcase(filename)
|
||||||
|
if self.dict.has_key(key):
|
||||||
|
edit = self.dict[key]
|
||||||
|
filename = edit.io.filename or filename
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def open(self, filename):
|
||||||
|
assert filename
|
||||||
filename = self.canonize(filename)
|
filename = self.canonize(filename)
|
||||||
if os.path.isdir(filename):
|
if os.path.isdir(filename):
|
||||||
tkMessageBox.showerror(
|
tkMessageBox.showerror(
|
||||||
|
@ -110,17 +51,10 @@ class FileList:
|
||||||
"New File",
|
"New File",
|
||||||
"Opening non-existent file %s" % `filename`,
|
"Opening non-existent file %s" % `filename`,
|
||||||
master=self.root)
|
master=self.root)
|
||||||
if edit and not edit.io.filename and edit.undo.get_saved():
|
return self.EditorWindow(self, filename, key)
|
||||||
# Reuse existing Untitled window for new file
|
|
||||||
edit.io.loadfile(filename)
|
def new(self):
|
||||||
self.dict[key] = edit
|
return self.EditorWindow(self)
|
||||||
self.inversedict[edit] = key
|
|
||||||
edit.wakeup()
|
|
||||||
return edit
|
|
||||||
else:
|
|
||||||
key = None
|
|
||||||
edit = self.EditorWindow(self, filename, key)
|
|
||||||
return edit
|
|
||||||
|
|
||||||
def new_callback(self, event):
|
def new_callback(self, event):
|
||||||
self.new()
|
self.new()
|
||||||
|
@ -189,6 +123,7 @@ class FileList:
|
||||||
|
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
|
from EditorWindow import fixwordbreaks
|
||||||
import sys
|
import sys
|
||||||
root = Tk()
|
root = Tk()
|
||||||
fixwordbreaks(root)
|
fixwordbreaks(root)
|
||||||
|
|
134
Tools/idle/GrepDialog.py
Normal file
134
Tools/idle/GrepDialog.py
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
import string
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import fnmatch
|
||||||
|
from Tkinter import *
|
||||||
|
import tkMessageBox
|
||||||
|
import SearchEngine
|
||||||
|
from SearchDialogBase import SearchDialogBase
|
||||||
|
|
||||||
|
def grep(text, io=None, flist=None):
|
||||||
|
root = text._root()
|
||||||
|
engine = SearchEngine.get(root)
|
||||||
|
if not hasattr(engine, "_grepdialog"):
|
||||||
|
engine._grepdialog = GrepDialog(root, engine, flist)
|
||||||
|
dialog = engine._grepdialog
|
||||||
|
dialog.open(io)
|
||||||
|
|
||||||
|
class GrepDialog(SearchDialogBase):
|
||||||
|
|
||||||
|
title = "Find in Files Dialog"
|
||||||
|
icon = "Grep"
|
||||||
|
needwrapbutton = 0
|
||||||
|
|
||||||
|
def __init__(self, root, engine, flist):
|
||||||
|
SearchDialogBase.__init__(self, root, engine)
|
||||||
|
self.flist = flist
|
||||||
|
self.globvar = StringVar(root)
|
||||||
|
self.recvar = BooleanVar(root)
|
||||||
|
|
||||||
|
def open(self, io=None):
|
||||||
|
SearchDialogBase.open(self, None)
|
||||||
|
if io:
|
||||||
|
path = io.filename or ""
|
||||||
|
else:
|
||||||
|
path = ""
|
||||||
|
dir, base = os.path.split(path)
|
||||||
|
head, tail = os.path.splitext(base)
|
||||||
|
if not tail:
|
||||||
|
tail = ".py"
|
||||||
|
self.globvar.set(os.path.join(dir, "*" + tail))
|
||||||
|
|
||||||
|
def create_entries(self):
|
||||||
|
SearchDialogBase.create_entries(self)
|
||||||
|
self.globent = self.make_entry("In files:", self.globvar)
|
||||||
|
|
||||||
|
def create_other_buttons(self):
|
||||||
|
f = self.make_frame()
|
||||||
|
|
||||||
|
btn = Checkbutton(f, anchor="w",
|
||||||
|
variable=self.recvar,
|
||||||
|
text="Recurse down subdirectories")
|
||||||
|
btn.pack(side="top", fill="both")
|
||||||
|
btn.select()
|
||||||
|
|
||||||
|
def create_command_buttons(self):
|
||||||
|
SearchDialogBase.create_command_buttons(self)
|
||||||
|
self.make_button("Search Files", self.default_command, 1)
|
||||||
|
|
||||||
|
def default_command(self, event=None):
|
||||||
|
prog = self.engine.getprog()
|
||||||
|
if not prog:
|
||||||
|
return
|
||||||
|
path = self.globvar.get()
|
||||||
|
if not path:
|
||||||
|
self.top.bell()
|
||||||
|
return
|
||||||
|
from OutputWindow import OutputWindow
|
||||||
|
save = sys.stdout
|
||||||
|
try:
|
||||||
|
sys.stdout = OutputWindow(self.flist)
|
||||||
|
self.grep_it(prog, path)
|
||||||
|
finally:
|
||||||
|
sys.stdout = save
|
||||||
|
|
||||||
|
def grep_it(self, prog, path):
|
||||||
|
dir, base = os.path.split(path)
|
||||||
|
list = self.findfiles(dir, base, self.recvar.get())
|
||||||
|
list.sort()
|
||||||
|
self.close()
|
||||||
|
pat = self.engine.getpat()
|
||||||
|
print "Searching %s in %s ..." % (pat, path)
|
||||||
|
hits = 0
|
||||||
|
for fn in list:
|
||||||
|
try:
|
||||||
|
f = open(fn)
|
||||||
|
except IOError, msg:
|
||||||
|
print msg
|
||||||
|
continue
|
||||||
|
lineno = 0
|
||||||
|
while 1:
|
||||||
|
block = f.readlines(100000)
|
||||||
|
if not block:
|
||||||
|
break
|
||||||
|
for line in block:
|
||||||
|
lineno = lineno + 1
|
||||||
|
if line[-1:] == '\n':
|
||||||
|
line = line[:-1]
|
||||||
|
if prog.search(line):
|
||||||
|
sys.stdout.write("%s: %s: %s\n" % (fn, lineno, line))
|
||||||
|
hits = hits + 1
|
||||||
|
if hits:
|
||||||
|
if hits == 1:
|
||||||
|
s = ""
|
||||||
|
else:
|
||||||
|
s = "s"
|
||||||
|
print "Found", hits, "hit%s." % s
|
||||||
|
print "(Hint: right-click to open locations.)"
|
||||||
|
else:
|
||||||
|
print "No hits."
|
||||||
|
|
||||||
|
def findfiles(self, dir, base, rec):
|
||||||
|
try:
|
||||||
|
names = os.listdir(dir or os.curdir)
|
||||||
|
except os.error, msg:
|
||||||
|
print msg
|
||||||
|
return []
|
||||||
|
list = []
|
||||||
|
subdirs = []
|
||||||
|
for name in names:
|
||||||
|
fn = os.path.join(dir, name)
|
||||||
|
if os.path.isdir(fn):
|
||||||
|
subdirs.append(fn)
|
||||||
|
else:
|
||||||
|
if fnmatch.fnmatch(name, base):
|
||||||
|
list.append(fn)
|
||||||
|
if rec:
|
||||||
|
for subdir in subdirs:
|
||||||
|
list.extend(self.findfiles(subdir, base, rec))
|
||||||
|
return list
|
||||||
|
|
||||||
|
def close(self, event=None):
|
||||||
|
if self.top:
|
||||||
|
self.top.grab_release()
|
||||||
|
self.top.withdraw()
|
|
@ -1,65 +0,0 @@
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from Tkinter import *
|
|
||||||
|
|
||||||
|
|
||||||
class HelpWindow:
|
|
||||||
|
|
||||||
helpfile = "help.txt"
|
|
||||||
helptitle = "Help Window"
|
|
||||||
|
|
||||||
def __init__(self, root=None):
|
|
||||||
if not root:
|
|
||||||
import Tkinter
|
|
||||||
root = Tkinter._default_root
|
|
||||||
if root:
|
|
||||||
self.top = top = Toplevel(root)
|
|
||||||
else:
|
|
||||||
self.top = top = root = Tk()
|
|
||||||
|
|
||||||
helpfile = self.helpfile
|
|
||||||
if not os.path.exists(helpfile):
|
|
||||||
base = os.path.basename(self.helpfile)
|
|
||||||
for dir in sys.path:
|
|
||||||
fullname = os.path.join(dir, base)
|
|
||||||
if os.path.exists(fullname):
|
|
||||||
helpfile = fullname
|
|
||||||
break
|
|
||||||
try:
|
|
||||||
f = open(helpfile)
|
|
||||||
data = f.read()
|
|
||||||
f.close()
|
|
||||||
except IOError, msg:
|
|
||||||
data = "Can't open the help file (%s)" % `helpfile`
|
|
||||||
|
|
||||||
top.protocol("WM_DELETE_WINDOW", self.close_command)
|
|
||||||
top.wm_title(self.helptitle)
|
|
||||||
|
|
||||||
self.close_button = Button(top, text="close",
|
|
||||||
command=self.close_command)
|
|
||||||
self.close_button.pack(side="bottom")
|
|
||||||
|
|
||||||
self.vbar = vbar = Scrollbar(top, name="vbar")
|
|
||||||
self.text = text = Text(top)
|
|
||||||
|
|
||||||
vbar["command"] = text.yview
|
|
||||||
text["yscrollcommand"] = vbar.set
|
|
||||||
|
|
||||||
vbar.pack(side="right", fill="y")
|
|
||||||
text.pack(side="left", fill="both", expand=1)
|
|
||||||
|
|
||||||
text.insert("1.0", data)
|
|
||||||
|
|
||||||
text.config(state="disabled")
|
|
||||||
text.see("1.0")
|
|
||||||
|
|
||||||
def close_command(self):
|
|
||||||
self.top.destroy()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
h = HelpWindow()
|
|
||||||
h.top.mainloop()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -2,20 +2,42 @@ import os
|
||||||
import tkFileDialog
|
import tkFileDialog
|
||||||
import tkMessageBox
|
import tkMessageBox
|
||||||
|
|
||||||
|
#$ event <<open-window-from-file>>
|
||||||
|
#$ win <Control-o>
|
||||||
|
#$ unix <Control-x><Control-f>
|
||||||
|
|
||||||
|
#$ event <<save-window>>
|
||||||
|
#$ win <Control-s>
|
||||||
|
#$ unix <Control-x><Control-s>
|
||||||
|
|
||||||
|
#$ event <<save-window-as-file>>
|
||||||
|
#$ win <Alt-s>
|
||||||
|
#$ unix <Control-x><Control-w>
|
||||||
|
|
||||||
|
#$ event <<save-copy-of-window-as-file>>
|
||||||
|
#$ win <Alt-Shift-s>
|
||||||
|
#$ unix <Control-x><w>
|
||||||
|
|
||||||
|
|
||||||
class IOBinding:
|
class IOBinding:
|
||||||
|
|
||||||
# Calls to non-standard text methods:
|
def __init__(self, editwin):
|
||||||
# reset_undo()
|
self.editwin = editwin
|
||||||
# set_saved(1)
|
self.text = editwin.text
|
||||||
|
|
||||||
def __init__(self, text):
|
|
||||||
self.text = text
|
|
||||||
self.text.bind("<<open-window-from-file>>", self.open)
|
self.text.bind("<<open-window-from-file>>", self.open)
|
||||||
self.text.bind("<<save-window>>", self.save)
|
self.text.bind("<<save-window>>", self.save)
|
||||||
self.text.bind("<<save-window-as-file>>", self.save_as)
|
self.text.bind("<<save-window-as-file>>", self.save_as)
|
||||||
self.text.bind("<<save-copy-of-window-as-file>>", self.save_a_copy)
|
self.text.bind("<<save-copy-of-window-as-file>>", self.save_a_copy)
|
||||||
|
|
||||||
|
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
|
filename_change_hook = None
|
||||||
|
|
||||||
def set_filename_change_hook(self, hook):
|
def set_filename_change_hook(self, hook):
|
||||||
|
@ -25,18 +47,29 @@ class IOBinding:
|
||||||
|
|
||||||
def set_filename(self, filename):
|
def set_filename(self, filename):
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.text.set_saved(1)
|
self.set_saved(1)
|
||||||
if self.filename_change_hook:
|
if self.filename_change_hook:
|
||||||
self.filename_change_hook()
|
self.filename_change_hook()
|
||||||
|
|
||||||
def open(self, event):
|
def open(self, event):
|
||||||
if not self.text.get_saved():
|
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()
|
reply = self.maybesave()
|
||||||
if reply == "cancel":
|
if reply == "cancel":
|
||||||
|
self.text.focus_set()
|
||||||
return "break"
|
return "break"
|
||||||
filename = self.askopenfile()
|
filename = self.askopenfile()
|
||||||
if filename:
|
if filename:
|
||||||
self.loadfile(filename)
|
self.loadfile(filename)
|
||||||
|
else:
|
||||||
|
self.text.focus_set()
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
def loadfile(self, filename):
|
def loadfile(self, filename):
|
||||||
|
@ -50,14 +83,14 @@ class IOBinding:
|
||||||
self.text.delete("1.0", "end")
|
self.text.delete("1.0", "end")
|
||||||
self.set_filename(None)
|
self.set_filename(None)
|
||||||
self.text.insert("1.0", chars)
|
self.text.insert("1.0", chars)
|
||||||
self.text.reset_undo()
|
self.reset_undo()
|
||||||
self.set_filename(filename)
|
self.set_filename(filename)
|
||||||
self.text.mark_set("insert", "1.0")
|
self.text.mark_set("insert", "1.0")
|
||||||
self.text.see("insert")
|
self.text.see("insert")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def maybesave(self):
|
def maybesave(self):
|
||||||
if self.text.get_saved():
|
if self.get_saved():
|
||||||
return "yes"
|
return "yes"
|
||||||
message = "Do you want to save %s before closing?" % (
|
message = "Do you want to save %s before closing?" % (
|
||||||
self.filename or "this untitled document")
|
self.filename or "this untitled document")
|
||||||
|
@ -70,8 +103,9 @@ class IOBinding:
|
||||||
reply = m.show()
|
reply = m.show()
|
||||||
if reply == "yes":
|
if reply == "yes":
|
||||||
self.save(None)
|
self.save(None)
|
||||||
if not self.text.get_saved():
|
if not self.get_saved():
|
||||||
reply = "cancel"
|
reply = "cancel"
|
||||||
|
self.text.focus_set()
|
||||||
return reply
|
return reply
|
||||||
|
|
||||||
def save(self, event):
|
def save(self, event):
|
||||||
|
@ -79,7 +113,8 @@ class IOBinding:
|
||||||
self.save_as(event)
|
self.save_as(event)
|
||||||
else:
|
else:
|
||||||
if self.writefile(self.filename):
|
if self.writefile(self.filename):
|
||||||
self.text.set_saved(1)
|
self.set_saved(1)
|
||||||
|
self.text.focus_set()
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
def save_as(self, event):
|
def save_as(self, event):
|
||||||
|
@ -87,22 +122,23 @@ class IOBinding:
|
||||||
if filename:
|
if filename:
|
||||||
if self.writefile(filename):
|
if self.writefile(filename):
|
||||||
self.set_filename(filename)
|
self.set_filename(filename)
|
||||||
self.text.set_saved(1)
|
self.set_saved(1)
|
||||||
|
self.text.focus_set()
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
def save_a_copy(self, event):
|
def save_a_copy(self, event):
|
||||||
filename = self.asksavefile()
|
filename = self.asksavefile()
|
||||||
if filename:
|
if filename:
|
||||||
self.writefile(filename)
|
self.writefile(filename)
|
||||||
|
self.text.focus_set()
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
def writefile(self, filename):
|
def writefile(self, filename):
|
||||||
|
self.fixlastline()
|
||||||
try:
|
try:
|
||||||
f = open(filename, "w")
|
f = open(filename, "w")
|
||||||
chars = self.text.get("1.0", "end-1c")
|
chars = self.text.get("1.0", "end-1c")
|
||||||
f.write(chars)
|
f.write(chars)
|
||||||
if chars and chars[-1] != "\n":
|
|
||||||
f.write("\n")
|
|
||||||
f.close()
|
f.close()
|
||||||
## print "saved to", `filename`
|
## print "saved to", `filename`
|
||||||
return 1
|
return 1
|
||||||
|
@ -111,11 +147,16 @@ class IOBinding:
|
||||||
master=self.text)
|
master=self.text)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
def fixlastline(self):
|
||||||
|
c = self.text.get("end-2c")
|
||||||
|
if c != '\n':
|
||||||
|
self.text.insert("end-1c", "\n")
|
||||||
|
|
||||||
opendialog = None
|
opendialog = None
|
||||||
savedialog = None
|
savedialog = None
|
||||||
|
|
||||||
filetypes = [
|
filetypes = [
|
||||||
("Python files", "*.py", "TEXT"),
|
("Python and text files", "*.py *.pyw *.txt", "TEXT"),
|
||||||
("All text files", "*", "TEXT"),
|
("All text files", "*", "TEXT"),
|
||||||
("All files", "*"),
|
("All files", "*"),
|
||||||
]
|
]
|
||||||
|
@ -129,10 +170,13 @@ class IOBinding:
|
||||||
|
|
||||||
def defaultfilename(self, mode="open"):
|
def defaultfilename(self, mode="open"):
|
||||||
if self.filename:
|
if self.filename:
|
||||||
dir, base = os.path.split(self.filename)
|
return os.path.split(self.filename)
|
||||||
else:
|
else:
|
||||||
dir = base = ""
|
try:
|
||||||
return dir, base
|
pwd = os.getcwd()
|
||||||
|
except os.error:
|
||||||
|
pwd = ""
|
||||||
|
return pwd, ""
|
||||||
|
|
||||||
def asksavefile(self):
|
def asksavefile(self):
|
||||||
dir, base = self.defaultfilename("save")
|
dir, base = self.defaultfilename("save")
|
||||||
|
@ -145,13 +189,30 @@ class IOBinding:
|
||||||
def test():
|
def test():
|
||||||
from Tkinter import *
|
from Tkinter import *
|
||||||
root = Tk()
|
root = Tk()
|
||||||
class MyText(Text):
|
class MyEditWin:
|
||||||
def reset_undo(self): pass
|
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 set_saved(self, flag): pass
|
||||||
text = MyText(root)
|
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.pack()
|
||||||
text.focus_set()
|
text.focus_set()
|
||||||
io = IOBinding(text)
|
editwin = MyEditWin(text)
|
||||||
|
io = IOBinding(editwin)
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
90
Tools/idle/OutputWindow.py
Normal file
90
Tools/idle/OutputWindow.py
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
from Tkinter import *
|
||||||
|
from EditorWindow import EditorWindow
|
||||||
|
import re
|
||||||
|
import tkMessageBox
|
||||||
|
|
||||||
|
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"):
|
||||||
|
self.text.insert(mark, str(s), tags)
|
||||||
|
self.text.see(mark)
|
||||||
|
self.text.update()
|
||||||
|
|
||||||
|
def writelines(self, l):
|
||||||
|
map(self.write, l)
|
||||||
|
|
||||||
|
# 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")
|
||||||
|
for prog in self.file_line_progs:
|
||||||
|
m = prog.search(line)
|
||||||
|
if m:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
tkMessageBox.showerror("No special line",
|
||||||
|
"The line you point at doesn't look like "
|
||||||
|
"a file name followed by a line number.",
|
||||||
|
master=self.text)
|
||||||
|
return
|
||||||
|
filename, lineno = m.group(1, 2)
|
||||||
|
try:
|
||||||
|
f = open(filename, "r")
|
||||||
|
f.close()
|
||||||
|
except IOError, msg:
|
||||||
|
self.text.bell()
|
||||||
|
return
|
||||||
|
edit = self.flist.open(filename)
|
||||||
|
try:
|
||||||
|
lineno = int(lineno)
|
||||||
|
except ValueError, msg:
|
||||||
|
self.text.bell()
|
||||||
|
return
|
||||||
|
edit.gotoline(lineno)
|
|
@ -1,86 +0,0 @@
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
|
|
||||||
from Tkinter import *
|
|
||||||
|
|
||||||
class PopupMenu:
|
|
||||||
|
|
||||||
def __init__(self, text, flist):
|
|
||||||
self.text = text
|
|
||||||
self.flist = flist
|
|
||||||
self.text.bind("<3>", self.right_menu_event)
|
|
||||||
|
|
||||||
rmenu = None
|
|
||||||
|
|
||||||
def right_menu_event(self, event):
|
|
||||||
if not self.rmenu:
|
|
||||||
self.make_menu()
|
|
||||||
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")
|
|
||||||
|
|
||||||
def make_menu(self):
|
|
||||||
rmenu = Menu(self.text, tearoff=0)
|
|
||||||
rmenu.add_command(label="Go to line from traceback",
|
|
||||||
command=self.goto_traceback_line)
|
|
||||||
#rmenu.add_command(label="Open stack viewer",
|
|
||||||
# command=self.open_stack_viewer)
|
|
||||||
#rmenu.add_command(label="Help", command=self.help)
|
|
||||||
self.rmenu = rmenu
|
|
||||||
|
|
||||||
file_line_pats = [
|
|
||||||
r'File "([^"]*)", line (\d+)',
|
|
||||||
r'([^\s]+)\((\d+)\)',
|
|
||||||
r'([^\s]+):\s*(\d+):',
|
|
||||||
]
|
|
||||||
|
|
||||||
file_line_progs = None
|
|
||||||
|
|
||||||
def goto_traceback_line(self):
|
|
||||||
if self.file_line_progs is None:
|
|
||||||
l = []
|
|
||||||
for pat in self.file_line_pats:
|
|
||||||
l.append(re.compile(pat))
|
|
||||||
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")
|
|
||||||
for prog in self.file_line_progs:
|
|
||||||
m = prog.search(line)
|
|
||||||
if m:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
self.text.bell()
|
|
||||||
return
|
|
||||||
filename, lineno = m.group(1, 2)
|
|
||||||
try:
|
|
||||||
f = open(filename, "r")
|
|
||||||
f.close()
|
|
||||||
except IOError, msg:
|
|
||||||
self.text.bell()
|
|
||||||
return
|
|
||||||
edit = self.flist.open(filename)
|
|
||||||
try:
|
|
||||||
lineno = int(lineno)
|
|
||||||
except ValueError, msg:
|
|
||||||
self.text.bell()
|
|
||||||
return
|
|
||||||
edit.gotoline(lineno)
|
|
||||||
|
|
||||||
def open_stack_viewer(self):
|
|
||||||
try:
|
|
||||||
sys.last_traceback
|
|
||||||
except:
|
|
||||||
print "No stack trace yet"
|
|
||||||
return
|
|
||||||
from StackViewer import StackBrowser
|
|
||||||
sv = StackBrowser(self.text._root(), self.flist)
|
|
||||||
|
|
||||||
def help(self):
|
|
||||||
from HelpWindow import HelpWindow
|
|
||||||
HelpWindow(root=self.flist.root)
|
|
|
@ -12,9 +12,10 @@ from code import InteractiveInterpreter
|
||||||
from Tkinter import *
|
from Tkinter import *
|
||||||
import tkMessageBox
|
import tkMessageBox
|
||||||
|
|
||||||
from EditorWindow import fixwordbreaks
|
from EditorWindow import EditorWindow, fixwordbreaks
|
||||||
from FileList import FileList, MultiEditorWindow, MultiIOBinding
|
from FileList import FileList
|
||||||
from ColorDelegator import ColorDelegator
|
from ColorDelegator import ColorDelegator
|
||||||
|
from OutputWindow import OutputWindow
|
||||||
|
|
||||||
# We need to patch linecache.checkcache, because we don't want it
|
# We need to patch linecache.checkcache, because we don't want it
|
||||||
# to throw away our <pyshell#...> entries.
|
# to throw away our <pyshell#...> entries.
|
||||||
|
@ -31,36 +32,54 @@ def linecache_checkcache(orig_checkcache=linecache.checkcache):
|
||||||
linecache.checkcache = linecache_checkcache
|
linecache.checkcache = linecache_checkcache
|
||||||
|
|
||||||
|
|
||||||
class PyShellEditorWindow(MultiEditorWindow):
|
# Note: <<newline-and-indent>> event is defined in AutoIndent.py
|
||||||
|
|
||||||
|
#$ event <<plain-newline-and-indent>>
|
||||||
|
#$ win <Control-j>
|
||||||
|
#$ unix <Control-j>
|
||||||
|
|
||||||
|
#$ event <<beginning-of-line>>
|
||||||
|
#$ win <Control-a>
|
||||||
|
#$ win <Home>
|
||||||
|
#$ unix <Control-a>
|
||||||
|
#$ unix <Home>
|
||||||
|
|
||||||
|
#$ event <<history-next>>
|
||||||
|
#$ win <Alt-n>
|
||||||
|
#$ unix <Alt-n>
|
||||||
|
|
||||||
|
#$ event <<history-previous>>
|
||||||
|
#$ win <Alt-p>
|
||||||
|
#$ unix <Alt-p>
|
||||||
|
|
||||||
|
#$ event <<interrupt-execution>>
|
||||||
|
#$ win <Control-c>
|
||||||
|
#$ unix <Control-c>
|
||||||
|
|
||||||
|
#$ event <<end-of-file>>
|
||||||
|
#$ win <Control-d>
|
||||||
|
#$ unix <Control-d>
|
||||||
|
|
||||||
|
#$ event <<open-stack-viewer>>
|
||||||
|
|
||||||
|
#$ event <<toggle-debugger>>
|
||||||
|
|
||||||
|
|
||||||
|
class PyShellEditorWindow(EditorWindow):
|
||||||
|
|
||||||
|
# Regular text edit window when a shell is present
|
||||||
|
# XXX ought to merge with regular editor window
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
apply(MultiEditorWindow.__init__, (self,) + args)
|
apply(EditorWindow.__init__, (self,) + args)
|
||||||
self.text.bind("<3>", self.right_menu_event)
|
self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)
|
||||||
|
self.text.bind("<<open-python-shell>>", self.flist.open_shell)
|
||||||
|
|
||||||
def fixedwindowsmenu(self, wmenu):
|
rmenu_specs = [
|
||||||
wmenu.add_command(label="Python Shell", command=self.flist.open_shell)
|
("Set breakpoint here", "<<set-breakpoint-here>>"),
|
||||||
wmenu.add_separator()
|
]
|
||||||
|
|
||||||
menu = None
|
def set_breakpoint_here(self, event=None):
|
||||||
|
|
||||||
def right_menu_event(self, event):
|
|
||||||
self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
|
|
||||||
if not self.menu:
|
|
||||||
self.make_menu()
|
|
||||||
menu = self.menu
|
|
||||||
iswin = sys.platform[:3] == 'win'
|
|
||||||
if iswin:
|
|
||||||
self.text.config(cursor="arrow")
|
|
||||||
menu.tk_popup(event.x_root, event.y_root)
|
|
||||||
if iswin:
|
|
||||||
self.text.config(cursor="ibeam")
|
|
||||||
|
|
||||||
def make_menu(self):
|
|
||||||
self.menu = menu = Menu(self.text, tearoff=0)
|
|
||||||
menu.add_command(label="Set breakpoint here",
|
|
||||||
command=self.set_breakpoint_here)
|
|
||||||
|
|
||||||
def set_breakpoint_here(self):
|
|
||||||
if not self.flist.pyshell or not self.flist.pyshell.interp.debugger:
|
if not self.flist.pyshell or not self.flist.pyshell.interp.debugger:
|
||||||
self.text.bell()
|
self.text.bell()
|
||||||
return
|
return
|
||||||
|
@ -69,11 +88,13 @@ class PyShellEditorWindow(MultiEditorWindow):
|
||||||
|
|
||||||
class PyShellFileList(FileList):
|
class PyShellFileList(FileList):
|
||||||
|
|
||||||
|
# File list when a shell is present
|
||||||
|
|
||||||
EditorWindow = PyShellEditorWindow
|
EditorWindow = PyShellEditorWindow
|
||||||
|
|
||||||
pyshell = None
|
pyshell = None
|
||||||
|
|
||||||
def open_shell(self):
|
def open_shell(self, event=None):
|
||||||
if self.pyshell:
|
if self.pyshell:
|
||||||
self.pyshell.wakeup()
|
self.pyshell.wakeup()
|
||||||
else:
|
else:
|
||||||
|
@ -82,40 +103,26 @@ class PyShellFileList(FileList):
|
||||||
return self.pyshell
|
return self.pyshell
|
||||||
|
|
||||||
|
|
||||||
class ModifiedIOBinding(MultiIOBinding):
|
|
||||||
|
|
||||||
def defaultfilename(self, mode="open"):
|
|
||||||
if self.filename:
|
|
||||||
return MultiIOBinding.defaultfilename(self, mode)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
pwd = os.getcwd()
|
|
||||||
except os.error:
|
|
||||||
pwd = ""
|
|
||||||
return pwd, ""
|
|
||||||
|
|
||||||
def open(self, event):
|
|
||||||
# Override base class method -- don't allow reusing this window
|
|
||||||
filename = self.askopenfile()
|
|
||||||
if filename:
|
|
||||||
self.flist.open(filename)
|
|
||||||
return "break"
|
|
||||||
|
|
||||||
def maybesave(self):
|
|
||||||
# Override base class method -- don't ask any questions
|
|
||||||
if self.text.get_saved():
|
|
||||||
return "yes"
|
|
||||||
else:
|
|
||||||
return "no"
|
|
||||||
|
|
||||||
|
|
||||||
class ModifiedColorDelegator(ColorDelegator):
|
class ModifiedColorDelegator(ColorDelegator):
|
||||||
|
|
||||||
|
# Colorizer for the shell window itself
|
||||||
|
|
||||||
def recolorize_main(self):
|
def recolorize_main(self):
|
||||||
self.tag_remove("TODO", "1.0", "iomark")
|
self.tag_remove("TODO", "1.0", "iomark")
|
||||||
self.tag_add("SYNC", "1.0", "iomark")
|
self.tag_add("SYNC", "1.0", "iomark")
|
||||||
ColorDelegator.recolorize_main(self)
|
ColorDelegator.recolorize_main(self)
|
||||||
|
|
||||||
|
tagdefs = ColorDelegator.tagdefs.copy()
|
||||||
|
|
||||||
|
tagdefs.update({
|
||||||
|
##"stdin": {"background": "yellow"},
|
||||||
|
"stdout": {"foreground": "blue"},
|
||||||
|
"stderr": {"foreground": "#007700"},
|
||||||
|
"console": {"foreground": "#770000"},
|
||||||
|
"ERROR": {"background": "#FF7777"},
|
||||||
|
None: {"foreground": "purple"}, # default
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class ModifiedInterpreter(InteractiveInterpreter):
|
class ModifiedInterpreter(InteractiveInterpreter):
|
||||||
|
|
||||||
|
@ -220,19 +227,17 @@ class ModifiedInterpreter(InteractiveInterpreter):
|
||||||
self.tkconsole.console.write(s)
|
self.tkconsole.console.write(s)
|
||||||
|
|
||||||
|
|
||||||
class PyShell(PyShellEditorWindow):
|
class PyShell(OutputWindow):
|
||||||
|
|
||||||
# Override classes
|
# Override classes
|
||||||
ColorDelegator = ModifiedColorDelegator
|
ColorDelegator = ModifiedColorDelegator
|
||||||
IOBinding = ModifiedIOBinding
|
|
||||||
|
|
||||||
# Override menu bar specs
|
# Override menu bar specs
|
||||||
menu_specs = PyShellEditorWindow.menu_specs[:]
|
menu_specs = PyShellEditorWindow.menu_specs[:]
|
||||||
menu_specs.insert(len(menu_specs)-1, ("debug", "Debug"))
|
menu_specs.insert(len(menu_specs)-2, ("debug", "_Debug"))
|
||||||
|
|
||||||
# New classes
|
# New classes
|
||||||
from History import History
|
from History import History
|
||||||
from PopupMenu import PopupMenu
|
|
||||||
|
|
||||||
def __init__(self, flist=None):
|
def __init__(self, flist=None):
|
||||||
self.interp = ModifiedInterpreter(self)
|
self.interp = ModifiedInterpreter(self)
|
||||||
|
@ -242,23 +247,24 @@ class PyShell(PyShellEditorWindow):
|
||||||
root.withdraw()
|
root.withdraw()
|
||||||
flist = PyShellFileList(root)
|
flist = PyShellFileList(root)
|
||||||
|
|
||||||
PyShellEditorWindow.__init__(self, flist, None, None)
|
OutputWindow.__init__(self, flist, None, None)
|
||||||
self.config_colors()
|
|
||||||
|
|
||||||
import __builtin__
|
import __builtin__
|
||||||
__builtin__.quit = __builtin__.exit = "To exit, type Ctrl-D."
|
__builtin__.quit = __builtin__.exit = "To exit, type Ctrl-D."
|
||||||
|
|
||||||
|
self.auto = self.extensions["AutoIndent"] # Required extension
|
||||||
self.auto.config(prefertabs=1)
|
self.auto.config(prefertabs=1)
|
||||||
|
|
||||||
text = self.text
|
text = self.text
|
||||||
|
text.configure(wrap="char")
|
||||||
text.bind("<<newline-and-indent>>", self.enter_callback)
|
text.bind("<<newline-and-indent>>", self.enter_callback)
|
||||||
text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
|
text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
|
||||||
text.bind("<<interrupt-execution>>", self.cancel_callback)
|
text.bind("<<interrupt-execution>>", self.cancel_callback)
|
||||||
text.bind("<<beginning-of-line>>", self.home_callback)
|
text.bind("<<beginning-of-line>>", self.home_callback)
|
||||||
text.bind("<<end-of-file>>", self.eof_callback)
|
text.bind("<<end-of-file>>", self.eof_callback)
|
||||||
text.bind("<<goto-traceback-line>>", self.goto_traceback_line)
|
|
||||||
text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
|
text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
|
||||||
text.bind("<<toggle-debugger>>", self.toggle_debugger)
|
text.bind("<<toggle-debugger>>", self.toggle_debugger)
|
||||||
|
text.bind("<<open-python-shell>>", self.flist.open_shell)
|
||||||
|
|
||||||
sys.stdout = PseudoFile(self, "stdout")
|
sys.stdout = PseudoFile(self, "stdout")
|
||||||
sys.stderr = PseudoFile(self, "stderr")
|
sys.stderr = PseudoFile(self, "stderr")
|
||||||
|
@ -266,25 +272,6 @@ class PyShell(PyShellEditorWindow):
|
||||||
self.console = PseudoFile(self, "console")
|
self.console = PseudoFile(self, "console")
|
||||||
|
|
||||||
self.history = self.History(self.text)
|
self.history = self.History(self.text)
|
||||||
self.popup = self.PopupMenu(self.text, self.flist)
|
|
||||||
|
|
||||||
tagdefs = {
|
|
||||||
##"stdin": {"background": "yellow"},
|
|
||||||
"stdout": {"foreground": "blue"},
|
|
||||||
"stderr": {"foreground": "#007700"},
|
|
||||||
"console": {"foreground": "red"},
|
|
||||||
"ERROR": {"background": "#FF7777"},
|
|
||||||
None: {"foreground": "purple"}, # default
|
|
||||||
}
|
|
||||||
|
|
||||||
def config_colors(self):
|
|
||||||
for tag, cnf in self.tagdefs.items():
|
|
||||||
if cnf:
|
|
||||||
if not tag:
|
|
||||||
apply(self.text.configure, (), cnf)
|
|
||||||
else:
|
|
||||||
apply(self.text.tag_configure, (tag,), cnf)
|
|
||||||
self.text.tag_raise("sel")
|
|
||||||
|
|
||||||
reading = 0
|
reading = 0
|
||||||
executing = 0
|
executing = 0
|
||||||
|
@ -362,14 +349,8 @@ class PyShell(PyShellEditorWindow):
|
||||||
# Override this so EditorWindow never removes the colorizer
|
# Override this so EditorWindow never removes the colorizer
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def saved_change_hook(self):
|
def short_title(self):
|
||||||
# Override this to get the title right
|
return "Python Shell"
|
||||||
title = "Python Shell"
|
|
||||||
if self.io.filename:
|
|
||||||
title = title + ": " + self.io.filename
|
|
||||||
if not self.undo.get_saved():
|
|
||||||
title = title + " *"
|
|
||||||
self.top.wm_title(title)
|
|
||||||
|
|
||||||
def begin(self):
|
def begin(self):
|
||||||
self.resetoutput()
|
self.resetoutput()
|
||||||
|
@ -457,7 +438,7 @@ class PyShell(PyShellEditorWindow):
|
||||||
self.text.insert("insert", "\n")
|
self.text.insert("insert", "\n")
|
||||||
self.text.see("insert")
|
self.text.see("insert")
|
||||||
else:
|
else:
|
||||||
self.auto.autoindent(event)
|
self.auto.auto_indent(event)
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
def enter_callback(self, event):
|
def enter_callback(self, event):
|
||||||
|
@ -468,7 +449,7 @@ class PyShell(PyShellEditorWindow):
|
||||||
try:
|
try:
|
||||||
sel = self.text.get("sel.first", "sel.last")
|
sel = self.text.get("sel.first", "sel.last")
|
||||||
if sel:
|
if sel:
|
||||||
if self.text.compare("self.last", "<=", "iomark"):
|
if self.text.compare("sel.last", "<=", "iomark"):
|
||||||
self.recall(sel)
|
self.recall(sel)
|
||||||
return "break"
|
return "break"
|
||||||
except:
|
except:
|
||||||
|
@ -492,7 +473,7 @@ class PyShell(PyShellEditorWindow):
|
||||||
# If we're in the current input before its last line,
|
# If we're in the current input before its last line,
|
||||||
# insert a newline right at the insert point
|
# insert a newline right at the insert point
|
||||||
if self.text.compare("insert", "<", "end-1c linestart"):
|
if self.text.compare("insert", "<", "end-1c linestart"):
|
||||||
self.auto.autoindent(event)
|
self.auto.auto_indent(event)
|
||||||
return "break"
|
return "break"
|
||||||
# We're in the last line; append a newline and submit it
|
# We're in the last line; append a newline and submit it
|
||||||
self.text.mark_set("insert", "end-1c")
|
self.text.mark_set("insert", "end-1c")
|
||||||
|
@ -500,7 +481,7 @@ class PyShell(PyShellEditorWindow):
|
||||||
self.text.insert("insert", "\n")
|
self.text.insert("insert", "\n")
|
||||||
self.text.see("insert")
|
self.text.see("insert")
|
||||||
else:
|
else:
|
||||||
self.auto.autoindent(event)
|
self.auto.auto_indent(event)
|
||||||
self.text.tag_add("stdin", "iomark", "end-1c")
|
self.text.tag_add("stdin", "iomark", "end-1c")
|
||||||
self.text.update_idletasks()
|
self.text.update_idletasks()
|
||||||
if self.reading:
|
if self.reading:
|
||||||
|
@ -546,48 +527,6 @@ class PyShell(PyShellEditorWindow):
|
||||||
raise KeyboardInterrupt
|
raise KeyboardInterrupt
|
||||||
return self._cancel_check
|
return self._cancel_check
|
||||||
|
|
||||||
file_line_pats = [
|
|
||||||
r'File "([^"]*)", line (\d+)',
|
|
||||||
r'([^\s]+)\((\d+)\)',
|
|
||||||
r'([^\s]+):\s*(\d+):',
|
|
||||||
]
|
|
||||||
|
|
||||||
file_line_progs = None
|
|
||||||
|
|
||||||
def goto_traceback_line(self, event=None):
|
|
||||||
if self.file_line_progs is None:
|
|
||||||
l = []
|
|
||||||
for pat in self.file_line_pats:
|
|
||||||
l.append(re.compile(pat))
|
|
||||||
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")
|
|
||||||
for prog in self.file_line_progs:
|
|
||||||
m = prog.search(line)
|
|
||||||
if m:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
tkMessageBox.showerror("No traceback line",
|
|
||||||
"The line you point at doesn't look "
|
|
||||||
"like an error message.",
|
|
||||||
master=self.text)
|
|
||||||
return
|
|
||||||
filename, lineno = m.group(1, 2)
|
|
||||||
try:
|
|
||||||
f = open(filename, "r")
|
|
||||||
f.close()
|
|
||||||
except IOError, msg:
|
|
||||||
self.text.bell()
|
|
||||||
return
|
|
||||||
edit = self.flist.open(filename)
|
|
||||||
try:
|
|
||||||
lineno = int(lineno)
|
|
||||||
except ValueError, msg:
|
|
||||||
self.text.bell()
|
|
||||||
return
|
|
||||||
edit.gotoline(lineno)
|
|
||||||
|
|
||||||
def open_stack_viewer(self, event=None):
|
def open_stack_viewer(self, event=None):
|
||||||
try:
|
try:
|
||||||
sys.last_traceback
|
sys.last_traceback
|
||||||
|
@ -618,26 +557,22 @@ class PyShell(PyShellEditorWindow):
|
||||||
self.text.mark_set("iomark", "end-1c")
|
self.text.mark_set("iomark", "end-1c")
|
||||||
sys.stdout.softspace = 0
|
sys.stdout.softspace = 0
|
||||||
|
|
||||||
def write(self, s):
|
def write(self, s, tags=()):
|
||||||
# Overrides base class write
|
self.text.mark_gravity("iomark", "right")
|
||||||
self.console.write(s)
|
OutputWindow.write(self, s, tags, "iomark")
|
||||||
|
self.text.mark_gravity("iomark", "left")
|
||||||
|
if self.canceled:
|
||||||
|
self.canceled = 0
|
||||||
|
raise KeyboardInterrupt
|
||||||
|
|
||||||
class PseudoFile:
|
class PseudoFile:
|
||||||
|
|
||||||
def __init__(self, interp, tags):
|
def __init__(self, shell, tags):
|
||||||
self.interp = interp
|
self.shell = shell
|
||||||
self.text = interp.text
|
|
||||||
self.tags = tags
|
self.tags = tags
|
||||||
|
|
||||||
def write(self, s):
|
def write(self, s):
|
||||||
self.text.mark_gravity("iomark", "right")
|
self.shell.write(s, self.tags)
|
||||||
self.text.insert("iomark", str(s), self.tags)
|
|
||||||
self.text.mark_gravity("iomark", "left")
|
|
||||||
self.text.see("iomark")
|
|
||||||
self.text.update()
|
|
||||||
if self.interp.canceled:
|
|
||||||
self.interp.canceled = 0
|
|
||||||
raise KeyboardInterrupt
|
|
||||||
|
|
||||||
def writelines(self, l):
|
def writelines(self, l):
|
||||||
map(self.write, l)
|
map(self.write, l)
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
IDLE 0.1 - 10/16/98
|
IDLE 0.2 - 01/01/99
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
This is a *very* early preliminary release of IDLE, my own attempt at
|
This is a *very* early preliminary release of IDLE, my own attempt at
|
||||||
a Tkinter-based IDE for Python. It currently has the following
|
a Tkinter-based IDE for Python. It has the following features:
|
||||||
features:
|
|
||||||
|
|
||||||
- multi-window text editor with multiple undo and Python colorizing
|
- multi-window text editor with multiple undo and Python colorizing
|
||||||
- Python shell (a.k.a. interactive interpreter) window subclass
|
- Python shell (a.k.a. interactive interpreter) window subclass
|
||||||
- debugger
|
- debugger
|
||||||
- 100% pure Python
|
- 100% pure Python
|
||||||
- works on Windows and Unix (should work on Mac too)
|
- works on Windows and Unix (probably works on Mac too)
|
||||||
|
|
||||||
The main program is in the file "idle"; on Windows you can use
|
The main program is in the file "idle"; on Windows you can use
|
||||||
idle.pyw to avoid popping up a DOS console. Any arguments passed are
|
idle.pyw to avoid popping up a DOS console. Any arguments passed are
|
||||||
interpreted as files that will be opened for editing.
|
interpreted as files that will be opened for editing.
|
||||||
|
|
||||||
IDLE requires Python 1.5.2, so it is currently only usable for PSA
|
IDLE requires Python 1.5.2, so it is currently only usable with the
|
||||||
members who have the latest 1.5.2 alpha release (a public beta release
|
Python 1.5.2 beta distribution (luckily, IDLE is bundled with Python
|
||||||
is due shortly).
|
1.5.2).
|
||||||
|
|
||||||
Please send feedback to the Python newsgroup, comp.lang.python.
|
Please send feedback to the Python newsgroup, comp.lang.python.
|
||||||
|
|
||||||
|
@ -27,46 +26,71 @@ Please send feedback to the Python newsgroup, comp.lang.python.
|
||||||
|
|
||||||
TO DO:
|
TO DO:
|
||||||
|
|
||||||
|
- "GO" command
|
||||||
|
- "Modularize" command
|
||||||
|
- command expansion from keywords, module contents, other buffers, etc.
|
||||||
- "Recent documents" menu item
|
- "Recent documents" menu item
|
||||||
- use platform specific default bindings
|
|
||||||
- title and Windows menu should have base filename first
|
|
||||||
- restructure state sensitive code to avoid testing flags all the time
|
|
||||||
- integrated debugger
|
|
||||||
- object browser instead of current stack viewer
|
|
||||||
- save some user state (e.g. window and cursor positions, bindings)
|
|
||||||
- make backups when saving
|
|
||||||
- check file mtimes at various points
|
|
||||||
- interface with RCS/CVS/Perforce ???
|
|
||||||
- more search options: case [in]sensitive, fwd/back, string/regex
|
|
||||||
- global query replace
|
|
||||||
- incremental search
|
|
||||||
- more emacsisms:
|
- more emacsisms:
|
||||||
|
- parentheses matching
|
||||||
- reindent, reformat text etc.
|
- reindent, reformat text etc.
|
||||||
- M-[, M-] to move by paragraphs
|
- M-[, M-] to move by paragraphs
|
||||||
- smart stuff with whitespace around Return
|
- smart stuff with whitespace around Return
|
||||||
- filter region?
|
- filter region?
|
||||||
- grep?
|
- incremental search?
|
||||||
|
- ^K should cut to buffer
|
||||||
|
- command to fill text paragraphs
|
||||||
|
- restructure state sensitive code to avoid testing flags all the time
|
||||||
|
- finish debugger
|
||||||
|
- object browser instead of current stack viewer
|
||||||
|
- persistent user state (e.g. window and cursor positions, bindings)
|
||||||
|
- make backups when saving
|
||||||
|
- check file mtimes at various points
|
||||||
|
- interface with RCS/CVS/Perforce ???
|
||||||
- status bar?
|
- status bar?
|
||||||
- better help?
|
- better help?
|
||||||
|
- don't open second class browser on same module
|
||||||
|
|
||||||
Details:
|
Details:
|
||||||
|
|
||||||
- when there's a selection, left/right arrow should go to either
|
- when there's a selection, left/right arrow should go to either
|
||||||
end of the selection
|
end of the selection
|
||||||
- ^O should honor autoindent
|
- ^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?)
|
||||||
|
|
||||||
Structural problems:
|
Structural problems:
|
||||||
|
|
||||||
- too much knowledge in FileList about EditorWindow (for example)
|
- too much knowledge in FileList about EditorWindow (for example)
|
||||||
- Several occurrences of scrollable listbox with title and certain
|
- Several occurrences of scrollable listbox with title and certain
|
||||||
behavior; should create base class to generalize this
|
behavior; should create base class to generalize this
|
||||||
- class browser could become an outline?
|
|
||||||
|
|
||||||
======================================================================
|
======================================================================
|
||||||
|
|
||||||
Comparison to PTUI
|
Comparison to PTUI
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
+ PTUI has a status line
|
||||||
|
|
||||||
|
+ PTUI's help is better (HTML!)
|
||||||
|
|
||||||
|
+ PTUI can attach a shell to any module
|
||||||
|
|
||||||
|
+ PTUI's auto indent is better
|
||||||
|
(understands that "if a: # blah, blah" opens a block)
|
||||||
|
|
||||||
|
+ IDLE requires 4x backspace to dedent a line
|
||||||
|
|
||||||
|
+ PTUI has more bells and whistles:
|
||||||
|
open multiple
|
||||||
|
append
|
||||||
|
modularize
|
||||||
|
examine
|
||||||
|
go
|
||||||
|
|
||||||
|
? PTUI's fontify is faster but synchronous (and still too slow);
|
||||||
|
does a lousy job if editing affects lines below
|
||||||
|
|
||||||
- PTUI's shell is worse:
|
- PTUI's shell is worse:
|
||||||
no coloring;
|
no coloring;
|
||||||
no editing of multi-line commands;
|
no editing of multi-line commands;
|
||||||
|
@ -76,34 +100,18 @@ Comparison to PTUI
|
||||||
no redo;
|
no redo;
|
||||||
one char at a time
|
one char at a time
|
||||||
|
|
||||||
- PTUI's framework is better:
|
|
||||||
status line
|
|
||||||
(not sure if I like the toolbar)
|
|
||||||
|
|
||||||
- PTUI's GUI is a tad ugly:
|
- PTUI's GUI is a tad ugly:
|
||||||
I don't like the multiple buffers in one window model
|
I don't like the multiple buffers in one window model;
|
||||||
|
I don't like the big buttons at the top of the widow
|
||||||
|
|
||||||
- PTUI's help is better (HTML!)
|
- PTUI lacks an integrated debugger
|
||||||
|
|
||||||
- PTUI's search/replace is better (more features)
|
- PTUI lacks a class browser
|
||||||
|
|
||||||
- PTUI's auto indent is better
|
- PTUI lacks many of IDLE's features:
|
||||||
(understands that "if a: # blah, blah" opens a block)
|
- expand word
|
||||||
|
- regular expression search
|
||||||
- PTUI's key bindings are a bit weird (DEL to dedent a line!?!?!?)
|
- search files (grep)
|
||||||
|
|
||||||
- PTUI's fontify is faster but synchronous (and still too slow);
|
|
||||||
also doesn't do as good a job if editing affects lines far below
|
|
||||||
|
|
||||||
- PTUI has more bells and whistles:
|
|
||||||
open multiple
|
|
||||||
append
|
|
||||||
zap tabs
|
|
||||||
fontify (you could argue it's not needed in my code)
|
|
||||||
comment/uncomment
|
|
||||||
modularize
|
|
||||||
examine
|
|
||||||
go
|
|
||||||
|
|
||||||
======================================================================
|
======================================================================
|
||||||
|
|
168
Tools/idle/ReplaceDialog.py
Normal file
168
Tools/idle/ReplaceDialog.py
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
import string
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import fnmatch
|
||||||
|
from Tkinter import *
|
||||||
|
import tkMessageBox
|
||||||
|
import SearchEngine
|
||||||
|
from SearchDialogBase import SearchDialogBase
|
||||||
|
|
||||||
|
def replace(text):
|
||||||
|
root = text._root()
|
||||||
|
engine = SearchEngine.get(root)
|
||||||
|
if not hasattr(engine, "_replacedialog"):
|
||||||
|
engine._replacedialog = ReplaceDialog(root, engine)
|
||||||
|
dialog = engine._replacedialog
|
||||||
|
dialog.open(text)
|
||||||
|
|
||||||
|
class ReplaceDialog(SearchDialogBase):
|
||||||
|
|
||||||
|
title = "Replace Dialog"
|
||||||
|
icon = "Replace"
|
||||||
|
|
||||||
|
def __init__(self, root, engine):
|
||||||
|
SearchDialogBase.__init__(self, root, engine)
|
||||||
|
self.replvar = StringVar(root)
|
||||||
|
|
||||||
|
def open(self, text):
|
||||||
|
SearchDialogBase.open(self, text)
|
||||||
|
try:
|
||||||
|
first = text.index("sel.first")
|
||||||
|
except TclError:
|
||||||
|
first = None
|
||||||
|
try:
|
||||||
|
last = text.index("sel.last")
|
||||||
|
except TclError:
|
||||||
|
last = None
|
||||||
|
first = first or text.index("insert")
|
||||||
|
last = last or first
|
||||||
|
self.show_hit(first, last)
|
||||||
|
self.ok = 1
|
||||||
|
|
||||||
|
def create_entries(self):
|
||||||
|
SearchDialogBase.create_entries(self)
|
||||||
|
self.replent = self.make_entry("Replace with:", self.replvar)
|
||||||
|
|
||||||
|
def create_command_buttons(self):
|
||||||
|
SearchDialogBase.create_command_buttons(self)
|
||||||
|
self.make_button("Find", self.find_it)
|
||||||
|
self.make_button("Replace", self.replace_it)
|
||||||
|
self.make_button("Replace+Find", self.default_command, 1)
|
||||||
|
self.make_button("Replace All", self.replace_all)
|
||||||
|
|
||||||
|
def find_it(self, event=None):
|
||||||
|
self.do_find(0)
|
||||||
|
|
||||||
|
def replace_it(self, event=None):
|
||||||
|
if self.do_find(self.ok):
|
||||||
|
self.do_replace()
|
||||||
|
|
||||||
|
def default_command(self, event=None):
|
||||||
|
if self.do_find(self.ok):
|
||||||
|
self.do_replace()
|
||||||
|
self.do_find(0)
|
||||||
|
|
||||||
|
def replace_all(self, event=None):
|
||||||
|
prog = self.engine.getprog()
|
||||||
|
if not prog:
|
||||||
|
return
|
||||||
|
repl = self.replvar.get()
|
||||||
|
text = self.text
|
||||||
|
res = self.engine.search_text(text, prog)
|
||||||
|
if not res:
|
||||||
|
text.bell()
|
||||||
|
return
|
||||||
|
text.tag_remove("sel", "1.0", "end")
|
||||||
|
text.tag_remove("hit", "1.0", "end")
|
||||||
|
line = res[0]
|
||||||
|
col = res[1].start()
|
||||||
|
if self.engine.iswrap():
|
||||||
|
line = 1
|
||||||
|
col = 0
|
||||||
|
ok = 1
|
||||||
|
first = last = None
|
||||||
|
# XXX ought to replace circular instead of top-to-bottom when wrapping
|
||||||
|
while 1:
|
||||||
|
res = self.engine.search_forward(text, prog, line, col, 0, ok)
|
||||||
|
if not res:
|
||||||
|
break
|
||||||
|
line, m = res
|
||||||
|
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||||
|
orig = m.group()
|
||||||
|
new = re.pcre_expand(m, repl)
|
||||||
|
i, j = m.span()
|
||||||
|
first = "%d.%d" % (line, i)
|
||||||
|
last = "%d.%d" % (line, j)
|
||||||
|
if new == orig:
|
||||||
|
text.mark_set("insert", last)
|
||||||
|
else:
|
||||||
|
text.mark_set("insert", first)
|
||||||
|
if first != last:
|
||||||
|
text.delete(first, last)
|
||||||
|
if new:
|
||||||
|
text.insert(first, new)
|
||||||
|
col = i + len(new)
|
||||||
|
ok = 0
|
||||||
|
if first and last:
|
||||||
|
self.show_hit(first, last)
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def do_find(self, ok=0):
|
||||||
|
if not self.engine.getprog():
|
||||||
|
return 0
|
||||||
|
text = self.text
|
||||||
|
res = self.engine.search_text(text, None, ok)
|
||||||
|
if not res:
|
||||||
|
text.bell()
|
||||||
|
return 0
|
||||||
|
line, m = res
|
||||||
|
i, j = m.span()
|
||||||
|
first = "%d.%d" % (line, i)
|
||||||
|
last = "%d.%d" % (line, j)
|
||||||
|
self.show_hit(first, last)
|
||||||
|
self.ok = 1
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def do_replace(self):
|
||||||
|
prog = self.engine.getprog()
|
||||||
|
if not prog:
|
||||||
|
return 0
|
||||||
|
text = self.text
|
||||||
|
try:
|
||||||
|
first = pos = text.index("sel.first")
|
||||||
|
last = text.index("sel.last")
|
||||||
|
except TclError:
|
||||||
|
pos = None
|
||||||
|
if not pos:
|
||||||
|
first = last = pos = text.index("insert")
|
||||||
|
line, col = SearchEngine.get_line_col(pos)
|
||||||
|
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||||
|
m = prog.match(chars, col)
|
||||||
|
if not prog:
|
||||||
|
return 0
|
||||||
|
new = re.pcre_expand(m, self.replvar.get())
|
||||||
|
text.mark_set("insert", first)
|
||||||
|
if m.group():
|
||||||
|
text.delete(first, last)
|
||||||
|
if new:
|
||||||
|
text.insert(first, new)
|
||||||
|
self.show_hit(first, text.index("insert"))
|
||||||
|
self.ok = 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def show_hit(self, first, last):
|
||||||
|
text = self.text
|
||||||
|
text.mark_set("insert", first)
|
||||||
|
text.tag_remove("sel", "1.0", "end")
|
||||||
|
text.tag_add("sel", first, last)
|
||||||
|
text.tag_remove("hit", "1.0", "end")
|
||||||
|
if first == last:
|
||||||
|
text.tag_add("hit", first)
|
||||||
|
else:
|
||||||
|
text.tag_add("hit", first, last)
|
||||||
|
text.see("insert")
|
||||||
|
text.update_idletasks()
|
||||||
|
|
||||||
|
def close(self, event=None):
|
||||||
|
SearchDialogBase.close(self, event)
|
||||||
|
self.text.tag_remove("hit", "1.0", "end")
|
38
Tools/idle/ScriptBinding.py
Normal file
38
Tools/idle/ScriptBinding.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import tkMessageBox
|
||||||
|
import os
|
||||||
|
import imp
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class ScriptBinding:
|
||||||
|
|
||||||
|
def __init__(self, editwin):
|
||||||
|
self.editwin = editwin
|
||||||
|
text = editwin.text
|
||||||
|
text.bind("<<run-module>>", self.run_module)
|
||||||
|
text.bind("<<run-script>>", self.run_script)
|
||||||
|
text.bind("<<new-shell>>", self.new_shell)
|
||||||
|
|
||||||
|
def run_module(self, event=None):
|
||||||
|
filename = self.editwin.io.filename
|
||||||
|
if not filename:
|
||||||
|
tkMessageBox.showerror("No file name",
|
||||||
|
"This window has no file name",
|
||||||
|
master=self.editwin.text)
|
||||||
|
return
|
||||||
|
modname, ext = os.path.splitext(os.path.basename(filename))
|
||||||
|
try:
|
||||||
|
mod = sys.modules[modname]
|
||||||
|
except KeyError:
|
||||||
|
mod = imp.new_module(modname)
|
||||||
|
sys.modules[modname] = mod
|
||||||
|
source = self.editwin.text.get("1.0", "end")
|
||||||
|
exec source in mod.__dict__
|
||||||
|
|
||||||
|
def run_script(self, event=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def new_shell(self, event=None):
|
||||||
|
import PyShell
|
||||||
|
# XXX Not enough: each shell takes over stdin/stdout/stderr...
|
||||||
|
pyshell = PyShell.PyShell(self.editwin.flist)
|
||||||
|
pyshell.begin()
|
|
@ -1,89 +1,96 @@
|
||||||
import string
|
|
||||||
import re
|
|
||||||
import tkSimpleDialog
|
import tkSimpleDialog
|
||||||
import tkMessageBox
|
|
||||||
|
###$ 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:
|
class SearchBinding:
|
||||||
|
|
||||||
def __init__(self, text):
|
windows_keydefs = {
|
||||||
self.text = text
|
'<<find-again>>': ['<Control-g>', '<F3>'],
|
||||||
self.pat = ""
|
'<<find-in-files>>': ['<Alt-F3>'],
|
||||||
self.prog = None
|
'<<find-selection>>': ['<Control-F3>'],
|
||||||
self.text.bind("<<find>>", self.find_event)
|
'<<find>>': ['<Control-f>'],
|
||||||
self.text.bind("<<find-next>>", self.find_next_event)
|
'<<replace>>': ['<Control-h>'],
|
||||||
self.text.bind("<<find-same>>", self.find_same_event)
|
'<<goto-line>>': ['<Alt-g>'],
|
||||||
self.text.bind("<<goto-line>>", self.goto_line_event)
|
}
|
||||||
|
|
||||||
|
unix_keydefs = {
|
||||||
|
'<<find-again>>': ['<Control-u><Control-s>'],
|
||||||
|
'<<find-selection>>': ['<Control-s>'],
|
||||||
|
'<<find>>': ['<Control-u><Control-u><Control-s>'],
|
||||||
|
'<<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):
|
def find_event(self, event):
|
||||||
default = self.text.get("self.first", "sel.last") or self.pat
|
import SearchDialog
|
||||||
new = tkSimpleDialog.askstring("Find",
|
SearchDialog.find(self.editwin.text)
|
||||||
"Regular Expression:",
|
|
||||||
initialvalue=default,
|
|
||||||
parent=self.text)
|
|
||||||
if not new:
|
|
||||||
return "break"
|
return "break"
|
||||||
self.pat = new
|
|
||||||
try:
|
|
||||||
self.prog = re.compile(self.pat)
|
|
||||||
except re.error, msg:
|
|
||||||
tkMessageBox.showerror("RE error", str(msg),
|
|
||||||
master=self.text)
|
|
||||||
return "break"
|
|
||||||
return self.find_next_event(event)
|
|
||||||
|
|
||||||
def find_same_event(self, event):
|
def find_again_event(self, event):
|
||||||
pat = self.text.get("sel.first", "sel.last")
|
import SearchDialog
|
||||||
if not pat:
|
SearchDialog.find_again(self.editwin.text)
|
||||||
return self.find_event(event)
|
|
||||||
self.pat = re.escape(pat)
|
|
||||||
self.prog = None
|
|
||||||
try:
|
|
||||||
self.prog = re.compile(self.pat)
|
|
||||||
except re.error, msg:
|
|
||||||
tkMessageBox.showerror("RE error", str(message),
|
|
||||||
master=self.text)
|
|
||||||
return "break"
|
return "break"
|
||||||
self.text.mark_set("insert", "sel.last")
|
|
||||||
return self.find_next_event(event)
|
|
||||||
|
|
||||||
def find_next_event(self, event):
|
def find_selection_event(self, event):
|
||||||
if not self.pat:
|
import SearchDialog
|
||||||
return self.find_event(event)
|
SearchDialog.find_selection(self.editwin.text)
|
||||||
if not self.prog:
|
|
||||||
self.text.bell()
|
|
||||||
##print "No program"
|
|
||||||
return "break"
|
return "break"
|
||||||
line, col = map(int,
|
|
||||||
string.split(self.text.index("insert"), "."))
|
def find_in_files_event(self, event):
|
||||||
chars = self.text.get("%d.0" % line, "%d.0" % (line+1))
|
import GrepDialog
|
||||||
while chars:
|
GrepDialog.grep(self.editwin.text, self.editwin.io, self.editwin.flist)
|
||||||
m = self.prog.search(chars, col)
|
return "break"
|
||||||
if m:
|
|
||||||
i, j = m.span()
|
def replace_event(self, event):
|
||||||
self.text.mark_set("insert",
|
import ReplaceDialog
|
||||||
"%d.%d" % (line, j))
|
ReplaceDialog.replace(self.editwin.text)
|
||||||
self.text.tag_remove("sel", "1.0", "end")
|
|
||||||
self.text.tag_add("sel",
|
|
||||||
"%d.%d" % (line, i),
|
|
||||||
"%d.%d" % (line, j))
|
|
||||||
self.text.see("insert")
|
|
||||||
break
|
|
||||||
line = line + 1
|
|
||||||
col = 0
|
|
||||||
chars = self.text.get("%d.0" % line, "%d.0" % (line+1))
|
|
||||||
else:
|
|
||||||
# Not found
|
|
||||||
self.text.bell()
|
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
def goto_line_event(self, event):
|
def goto_line_event(self, event):
|
||||||
|
print event
|
||||||
|
text = self.editwin.text
|
||||||
lineno = tkSimpleDialog.askinteger("Goto",
|
lineno = tkSimpleDialog.askinteger("Goto",
|
||||||
"Go to line number:",
|
"Go to line number:",
|
||||||
parent=self.text)
|
parent=text)
|
||||||
if lineno is None:
|
if lineno is None:
|
||||||
return "break"
|
return "break"
|
||||||
if lineno <= 0:
|
if lineno <= 0:
|
||||||
self.text.bell()
|
text.bell()
|
||||||
return "break"
|
return "break"
|
||||||
self.text.mark_set("insert", "%d.0" % lineno)
|
text.mark_set("insert", "%d.0" % lineno)
|
||||||
self.text.see("insert")
|
text.see("insert")
|
||||||
|
|
59
Tools/idle/SearchDialog.py
Normal file
59
Tools/idle/SearchDialog.py
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
from Tkinter import *
|
||||||
|
import SearchEngine
|
||||||
|
from SearchDialogBase import SearchDialogBase
|
||||||
|
|
||||||
|
|
||||||
|
def _setup(text):
|
||||||
|
root = text._root()
|
||||||
|
engine = SearchEngine.get(root)
|
||||||
|
if not hasattr(engine, "_searchdialog"):
|
||||||
|
engine._searchdialog = SearchDialog(root, engine)
|
||||||
|
return engine._searchdialog
|
||||||
|
|
||||||
|
def find(text):
|
||||||
|
return _setup(text).open(text)
|
||||||
|
|
||||||
|
def find_again(text):
|
||||||
|
return _setup(text).find_again(text)
|
||||||
|
|
||||||
|
def find_selection(text):
|
||||||
|
return _setup(text).find_selection(text)
|
||||||
|
|
||||||
|
class SearchDialog(SearchDialogBase):
|
||||||
|
|
||||||
|
def create_widgets(self):
|
||||||
|
f = SearchDialogBase.create_widgets(self)
|
||||||
|
self.make_button("Find", self.default_command, 1)
|
||||||
|
|
||||||
|
def default_command(self, event=None):
|
||||||
|
if not self.engine.getprog():
|
||||||
|
return
|
||||||
|
if self.find_again(self.text):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def find_again(self, text):
|
||||||
|
if not self.engine.getpat():
|
||||||
|
self.open(text)
|
||||||
|
return 0
|
||||||
|
if not self.engine.getprog():
|
||||||
|
return 0
|
||||||
|
res = self.engine.search_text(text)
|
||||||
|
if res:
|
||||||
|
line, m = res
|
||||||
|
i, j = m.span()
|
||||||
|
first = "%d.%d" % (line, i)
|
||||||
|
last = "%d.%d" % (line, j)
|
||||||
|
text.tag_remove("sel", "1.0", "end")
|
||||||
|
text.tag_add("sel", first, last)
|
||||||
|
text.mark_set("insert", self.engine.isback() and first or last)
|
||||||
|
text.see("insert")
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
text.bell()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def find_selection(self, text):
|
||||||
|
pat = text.get("sel.first", "sel.last")
|
||||||
|
if pat:
|
||||||
|
self.engine.setcookedpat(pat)
|
||||||
|
return self.find_again(text)
|
129
Tools/idle/SearchDialogBase.py
Normal file
129
Tools/idle/SearchDialogBase.py
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
import string
|
||||||
|
from Tkinter import *
|
||||||
|
|
||||||
|
class SearchDialogBase:
|
||||||
|
|
||||||
|
title = "Search Dialog"
|
||||||
|
icon = "Search"
|
||||||
|
needwrapbutton = 1
|
||||||
|
|
||||||
|
def __init__(self, root, engine):
|
||||||
|
self.root = root
|
||||||
|
self.engine = engine
|
||||||
|
self.top = None
|
||||||
|
|
||||||
|
def open(self, text):
|
||||||
|
self.text = text
|
||||||
|
if not self.top:
|
||||||
|
self.create_widgets()
|
||||||
|
else:
|
||||||
|
self.top.deiconify()
|
||||||
|
self.top.tkraise()
|
||||||
|
self.ent.focus_set()
|
||||||
|
self.ent.selection_range(0, "end")
|
||||||
|
self.ent.icursor(0)
|
||||||
|
self.top.grab_set()
|
||||||
|
|
||||||
|
def close(self, event=None):
|
||||||
|
if self.top:
|
||||||
|
self.top.grab_release()
|
||||||
|
self.top.withdraw()
|
||||||
|
|
||||||
|
def create_widgets(self):
|
||||||
|
top = Toplevel(self.root)
|
||||||
|
top.bind("<Return>", self.default_command)
|
||||||
|
top.bind("<Escape>", self.close)
|
||||||
|
top.protocol("WM_DELETE_WINDOW", self.close)
|
||||||
|
top.wm_title(self.title)
|
||||||
|
top.wm_iconname(self.icon)
|
||||||
|
self.top = top
|
||||||
|
|
||||||
|
self.row = 0
|
||||||
|
self.top.grid_columnconfigure(0, weight=0)
|
||||||
|
self.top.grid_columnconfigure(1, weight=100)
|
||||||
|
|
||||||
|
self.create_entries()
|
||||||
|
self.create_option_buttons()
|
||||||
|
self.create_other_buttons()
|
||||||
|
return self.create_command_buttons()
|
||||||
|
|
||||||
|
def make_entry(self, label, var):
|
||||||
|
l = Label(self.top, text=label)
|
||||||
|
l.grid(row=self.row, col=0, sticky="w")
|
||||||
|
e = Entry(self.top, textvariable=var, exportselection=0)
|
||||||
|
e.grid(row=self.row, col=1, sticky="we")
|
||||||
|
self.row = self.row + 1
|
||||||
|
return e
|
||||||
|
|
||||||
|
def make_frame(self):
|
||||||
|
f = Frame(self.top)
|
||||||
|
f.grid(row=self.row, col=0, columnspan=2, sticky="we")
|
||||||
|
self.row = self.row + 1
|
||||||
|
return f
|
||||||
|
|
||||||
|
def make_button(self, label, command, isdef=0, side="left"):
|
||||||
|
b = Button(self.buttonframe,
|
||||||
|
text=label, command=command,
|
||||||
|
default=isdef and "active" or "normal")
|
||||||
|
b.pack(side=side)
|
||||||
|
return b
|
||||||
|
|
||||||
|
def create_entries(self):
|
||||||
|
self.ent = self.make_entry("Find:", self.engine.patvar)
|
||||||
|
|
||||||
|
def create_option_buttons(self):
|
||||||
|
f = self.make_frame()
|
||||||
|
|
||||||
|
btn = Checkbutton(f, anchor="w",
|
||||||
|
variable=self.engine.revar,
|
||||||
|
text="Regular expression")
|
||||||
|
btn.pack(side="left", fill="both")
|
||||||
|
if self.engine.isre():
|
||||||
|
btn.select()
|
||||||
|
|
||||||
|
btn = Checkbutton(f, anchor="w",
|
||||||
|
variable=self.engine.casevar,
|
||||||
|
text="Match case")
|
||||||
|
btn.pack(side="left", fill="both")
|
||||||
|
if self.engine.iscase():
|
||||||
|
btn.select()
|
||||||
|
|
||||||
|
btn = Checkbutton(f, anchor="w",
|
||||||
|
variable=self.engine.wordvar,
|
||||||
|
text="Whole word")
|
||||||
|
btn.pack(side="left", fill="both")
|
||||||
|
if self.engine.isword():
|
||||||
|
btn.select()
|
||||||
|
|
||||||
|
if self.needwrapbutton:
|
||||||
|
btn = Checkbutton(f, anchor="w",
|
||||||
|
variable=self.engine.wrapvar,
|
||||||
|
text="Wrap around")
|
||||||
|
btn.pack(side="left", fill="both")
|
||||||
|
if self.engine.iswrap():
|
||||||
|
btn.select()
|
||||||
|
|
||||||
|
def create_other_buttons(self):
|
||||||
|
f = self.make_frame()
|
||||||
|
|
||||||
|
lbl = Label(f, text="Direction: ")
|
||||||
|
lbl.pack(side="left")
|
||||||
|
|
||||||
|
btn = Radiobutton(f, anchor="w",
|
||||||
|
variable=self.engine.backvar, value=1,
|
||||||
|
text="Up")
|
||||||
|
btn.pack(side="left", fill="both")
|
||||||
|
if self.engine.isback():
|
||||||
|
btn.select()
|
||||||
|
|
||||||
|
btn = Radiobutton(f, anchor="w",
|
||||||
|
variable=self.engine.backvar, value=0,
|
||||||
|
text="Down")
|
||||||
|
btn.pack(side="left", fill="both")
|
||||||
|
if not self.engine.isback():
|
||||||
|
btn.select()
|
||||||
|
|
||||||
|
def create_command_buttons(self):
|
||||||
|
f = self.buttonframe = self.make_frame()
|
||||||
|
b = self.make_button("close", self.close, side="right")
|
||||||
|
b.lower()
|
214
Tools/idle/SearchEngine.py
Normal file
214
Tools/idle/SearchEngine.py
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
import string
|
||||||
|
import re
|
||||||
|
from Tkinter import *
|
||||||
|
import tkMessageBox
|
||||||
|
|
||||||
|
def get(root):
|
||||||
|
if not hasattr(root, "_searchengine"):
|
||||||
|
root._searchengine = SearchEngine(root)
|
||||||
|
# XXX This will never garbage-collect -- who cares
|
||||||
|
return root._searchengine
|
||||||
|
|
||||||
|
class SearchEngine:
|
||||||
|
|
||||||
|
def __init__(self, root):
|
||||||
|
self.root = root
|
||||||
|
# State shared by search, replace, and grep;
|
||||||
|
# the search dialogs bind these to UI elements.
|
||||||
|
self.patvar = StringVar(root) # search pattern
|
||||||
|
self.revar = BooleanVar(root) # regular expression?
|
||||||
|
self.casevar = BooleanVar(root) # match case?
|
||||||
|
self.wordvar = BooleanVar(root) # match whole word?
|
||||||
|
self.wrapvar = BooleanVar(root) # wrap around buffer?
|
||||||
|
self.wrapvar.set(1) # (on by default)
|
||||||
|
self.backvar = BooleanVar(root) # search backwards?
|
||||||
|
|
||||||
|
# Access methods
|
||||||
|
|
||||||
|
def getpat(self):
|
||||||
|
return self.patvar.get()
|
||||||
|
|
||||||
|
def setpat(self, pat):
|
||||||
|
self.patvar.set(pat)
|
||||||
|
|
||||||
|
def isre(self):
|
||||||
|
return self.revar.get()
|
||||||
|
|
||||||
|
def iscase(self):
|
||||||
|
return self.casevar.get()
|
||||||
|
|
||||||
|
def isword(self):
|
||||||
|
return self.wordvar.get()
|
||||||
|
|
||||||
|
def iswrap(self):
|
||||||
|
return self.wrapvar.get()
|
||||||
|
|
||||||
|
def isback(self):
|
||||||
|
return self.backvar.get()
|
||||||
|
|
||||||
|
# Higher level access methods
|
||||||
|
|
||||||
|
def getcookedpat(self):
|
||||||
|
pat = self.getpat()
|
||||||
|
if not self.isre():
|
||||||
|
pat = re.escape(pat)
|
||||||
|
if self.isword():
|
||||||
|
pat = r"\b%s\b" % pat
|
||||||
|
return pat
|
||||||
|
|
||||||
|
def getprog(self):
|
||||||
|
pat = self.getpat()
|
||||||
|
if not pat:
|
||||||
|
self.report_error(pat, "Empty regular expression")
|
||||||
|
return None
|
||||||
|
pat = self.getcookedpat()
|
||||||
|
flags = 0
|
||||||
|
if not self.iscase():
|
||||||
|
flags = flags | re.IGNORECASE
|
||||||
|
try:
|
||||||
|
prog = re.compile(pat, flags)
|
||||||
|
except re.error, what:
|
||||||
|
try:
|
||||||
|
msg, col = what
|
||||||
|
except:
|
||||||
|
msg = str(what)
|
||||||
|
col = -1
|
||||||
|
self.report_error(pat, msg, col)
|
||||||
|
return None
|
||||||
|
return prog
|
||||||
|
|
||||||
|
def report_error(self, pat, msg, col=-1):
|
||||||
|
# Derived class could overrid this with something fancier
|
||||||
|
msg = "Error: " + str(msg)
|
||||||
|
if pat:
|
||||||
|
msg = msg + "\np\Pattern: " + str(pat)
|
||||||
|
if col >= 0:
|
||||||
|
msg = msg + "\nOffset: " + str(col)
|
||||||
|
tkMessageBox.showerror("Regular expression error",
|
||||||
|
msg, master=self.root)
|
||||||
|
|
||||||
|
def setcookedpat(self, pat):
|
||||||
|
if self.isre():
|
||||||
|
pat = re.escape(pat)
|
||||||
|
self.setpat(pat)
|
||||||
|
|
||||||
|
def search_text(self, text, prog=None, ok=0):
|
||||||
|
"""Search a text widget for the pattern.
|
||||||
|
|
||||||
|
If prog is given, it should be the precompiled pattern.
|
||||||
|
Return a tuple (lineno, matchobj); None if not found.
|
||||||
|
|
||||||
|
This obeys the wrap and direction (back) settings.
|
||||||
|
|
||||||
|
The search starts at the selection (if there is one) or
|
||||||
|
at the insert mark (otherwise). If the search is forward,
|
||||||
|
it starts at the right of the selection; for a backward
|
||||||
|
search, it starts at the left end. An empty match exactly
|
||||||
|
at either end of the selection (or at the insert mark if
|
||||||
|
there is no selection) is ignored unless the ok flag is true
|
||||||
|
-- this is done to guarantee progress.
|
||||||
|
|
||||||
|
If the search is allowed to wrap around, it will return the
|
||||||
|
original selection if (and only if) it is the only match.
|
||||||
|
|
||||||
|
XXX When wrapping around and failing to find anything, the
|
||||||
|
portion of the text after the selection is searched twice :-(
|
||||||
|
"""
|
||||||
|
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):
|
||||||
|
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
|
||||||
|
col = 0
|
||||||
|
ok = 1
|
||||||
|
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||||
|
if not chars and wrap:
|
||||||
|
wrap = 0
|
||||||
|
line = 1
|
||||||
|
chars = text.get("1.0", "2.0")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def search_backward(self, text, prog, line, col, wrap, ok=0):
|
||||||
|
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||||
|
while 1:
|
||||||
|
m = search_reverse(prog, chars[:-1], col)
|
||||||
|
if m:
|
||||||
|
i, j = m.span()
|
||||||
|
if ok or m.start() < col:
|
||||||
|
return line, m
|
||||||
|
line = line - 1
|
||||||
|
ok = 1
|
||||||
|
if line <= 0:
|
||||||
|
if not wrap:
|
||||||
|
break
|
||||||
|
wrap = 0
|
||||||
|
pos = text.index("end-1c")
|
||||||
|
line, col = map(int, string.split(pos, "."))
|
||||||
|
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||||
|
col = len(chars) - 1
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Helper to search backwards in a string.
|
||||||
|
# (Optimized for the case where the pattern isn't found.)
|
||||||
|
|
||||||
|
def search_reverse(prog, chars, col):
|
||||||
|
m = prog.search(chars)
|
||||||
|
if not m:
|
||||||
|
return None
|
||||||
|
found = None
|
||||||
|
i, j = m.span()
|
||||||
|
while i < col and j <= col:
|
||||||
|
found = m
|
||||||
|
if i == j:
|
||||||
|
j = j+1
|
||||||
|
m = prog.search(chars, j)
|
||||||
|
if not m:
|
||||||
|
break
|
||||||
|
i, j = m.span()
|
||||||
|
return found
|
||||||
|
|
||||||
|
# Helper to get selection end points, defaulting to insert mark.
|
||||||
|
# Return a tuple of indices ("line.col" strings).
|
||||||
|
|
||||||
|
def get_selection(text):
|
||||||
|
try:
|
||||||
|
first = text.index("sel.first")
|
||||||
|
last = text.index("sel.last")
|
||||||
|
except TclError:
|
||||||
|
first = last = None
|
||||||
|
if not first:
|
||||||
|
first = text.index("insert")
|
||||||
|
if not last:
|
||||||
|
last = first
|
||||||
|
return first, last
|
||||||
|
|
||||||
|
# Helper to parse a text index into a (line, col) tuple.
|
||||||
|
|
||||||
|
def get_line_col(index):
|
||||||
|
line, col = map(int, string.split(index, ".")) # Fails on invalid index
|
||||||
|
return line, col
|
|
@ -4,6 +4,7 @@ import os
|
||||||
from Tkinter import *
|
from Tkinter import *
|
||||||
import linecache
|
import linecache
|
||||||
from repr import Repr
|
from repr import Repr
|
||||||
|
from WindowList import ListedToplevel
|
||||||
|
|
||||||
from ScrolledList import ScrolledList
|
from ScrolledList import ScrolledList
|
||||||
|
|
||||||
|
@ -11,9 +12,10 @@ from ScrolledList import ScrolledList
|
||||||
class StackBrowser:
|
class StackBrowser:
|
||||||
|
|
||||||
def __init__(self, root, flist, stack=None):
|
def __init__(self, root, flist, stack=None):
|
||||||
self.top = top = Toplevel(root)
|
self.top = top = ListedToplevel(root)
|
||||||
top.protocol("WM_DELETE_WINDOW", self.close)
|
top.protocol("WM_DELETE_WINDOW", self.close)
|
||||||
top.wm_title("Stack viewer")
|
top.wm_title("Stack viewer")
|
||||||
|
top.wm_iconname("Stack")
|
||||||
# Create help label
|
# Create help label
|
||||||
self.helplabel = Label(top,
|
self.helplabel = Label(top,
|
||||||
text="Click once to view variables; twice for source",
|
text="Click once to view variables; twice for source",
|
||||||
|
|
|
@ -3,6 +3,18 @@ import string
|
||||||
from Tkinter import *
|
from Tkinter import *
|
||||||
from Delegator import Delegator
|
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):
|
class UndoDelegator(Delegator):
|
||||||
|
|
||||||
|
|
53
Tools/idle/WindowList.py
Normal file
53
Tools/idle/WindowList.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
from Tkinter import *
|
||||||
|
|
||||||
|
class WindowList:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.dict = {}
|
||||||
|
|
||||||
|
def add(self, window):
|
||||||
|
self.dict[str(window)] = window
|
||||||
|
|
||||||
|
def delete(self, window):
|
||||||
|
try:
|
||||||
|
del self.dict[str(window)]
|
||||||
|
except KeyError:
|
||||||
|
# Sometimes, destroy() is called twice
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_windows_to_menu(self, menu):
|
||||||
|
list = []
|
||||||
|
for key in self.dict.keys():
|
||||||
|
window = self.dict[key]
|
||||||
|
title = window.get_title()
|
||||||
|
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)
|
||||||
|
|
||||||
|
registry = WindowList()
|
||||||
|
|
||||||
|
def add_windows_to_menu(menu):
|
||||||
|
registry.add_windows_to_menu(menu)
|
||||||
|
|
||||||
|
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):
|
||||||
|
self.tkraise()
|
||||||
|
self.wm_deiconify()
|
||||||
|
self.focus_set()
|
35
Tools/idle/ZoomHeight.py
Normal file
35
Tools/idle/ZoomHeight.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# Sample extension: zoom a window to maximum height
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
class ZoomHeight:
|
||||||
|
|
||||||
|
menudefs = [
|
||||||
|
('windows', [
|
||||||
|
('_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):
|
||||||
|
top = self.editwin.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())
|
||||||
|
height = top.winfo_screenheight() - 72
|
||||||
|
newgeom = "%dx%d+%d+%d" % (width, height, x, 0)
|
||||||
|
if geom == newgeom:
|
||||||
|
newgeom = ""
|
||||||
|
top.wm_geometry(newgeom)
|
93
Tools/idle/eventparse.py
Normal file
93
Tools/idle/eventparse.py
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
|
||||||
|
"""Parse event definitions out of comments in source files."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import string
|
||||||
|
import getopt
|
||||||
|
import glob
|
||||||
|
import fileinput
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
def main():
|
||||||
|
hits = []
|
||||||
|
sublist = []
|
||||||
|
args = sys.argv[1:]
|
||||||
|
if not args:
|
||||||
|
args = filter(lambda s: 'A' <= s[0] <= 'Z', glob.glob("*.py"))
|
||||||
|
if not args:
|
||||||
|
print "No arguments, no [A-Z]*.py files."
|
||||||
|
return 1
|
||||||
|
for line in fileinput.input(args):
|
||||||
|
if line[:2] == '#$':
|
||||||
|
if not sublist:
|
||||||
|
sublist.append('file %s' % fileinput.filename())
|
||||||
|
sublist.append('line %d' % fileinput.lineno())
|
||||||
|
sublist.append(string.strip(line[2:-1]))
|
||||||
|
else:
|
||||||
|
if sublist:
|
||||||
|
hits.append(sublist)
|
||||||
|
sublist = []
|
||||||
|
if sublist:
|
||||||
|
hits.append(sublist)
|
||||||
|
sublist = []
|
||||||
|
dd = {}
|
||||||
|
for sublist in hits:
|
||||||
|
d = {}
|
||||||
|
for line in sublist:
|
||||||
|
words = string.split(line, None, 1)
|
||||||
|
if len(words) != 2:
|
||||||
|
continue
|
||||||
|
tag = words[0]
|
||||||
|
l = d.get(tag, [])
|
||||||
|
l.append(words[1])
|
||||||
|
d[tag] = l
|
||||||
|
if d.has_key('event'):
|
||||||
|
keys = d['event']
|
||||||
|
if len(keys) != 1:
|
||||||
|
print "Multiple event keys in", d
|
||||||
|
print 'File "%s", line %d' % (d['file'], d['line'])
|
||||||
|
key = keys[0]
|
||||||
|
if dd.has_key(key):
|
||||||
|
print "Duplicate event in", d
|
||||||
|
print 'File "%s", line %d' % (d['file'], d['line'])
|
||||||
|
return
|
||||||
|
dd[key] = d
|
||||||
|
else:
|
||||||
|
print "No event key in", d
|
||||||
|
print 'File "%s", line %d' % (d['file'], d['line'])
|
||||||
|
winevents = getevents(dd, "win")
|
||||||
|
unixevents = getevents(dd, "unix")
|
||||||
|
save = sys.stdout
|
||||||
|
f = open("keydefs.py", "w")
|
||||||
|
try:
|
||||||
|
sys.stdout = f
|
||||||
|
print "windows_keydefs = \\"
|
||||||
|
pprint.pprint(winevents)
|
||||||
|
print
|
||||||
|
print "unix_keydefs = \\"
|
||||||
|
pprint.pprint(unixevents)
|
||||||
|
finally:
|
||||||
|
sys.stdout = save
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def getevents(dd, key):
|
||||||
|
res = {}
|
||||||
|
events = dd.keys()
|
||||||
|
events.sort()
|
||||||
|
for e in events:
|
||||||
|
d = dd[e]
|
||||||
|
if d.has_key(key) or d.has_key("all"):
|
||||||
|
list = []
|
||||||
|
for x in d.get(key, []) + d.get("all", []):
|
||||||
|
list.append(x)
|
||||||
|
if key == "unix" and x[:5] == "<Alt-":
|
||||||
|
x = "<Meta-" + x[5:]
|
||||||
|
list.append(x)
|
||||||
|
res[e] = list
|
||||||
|
return res
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
9
Tools/idle/extend.py
Normal file
9
Tools/idle/extend.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# IDLE extensions to be loaded by default (see extend.txt).
|
||||||
|
# Edit this file to configure your set of IDLE extensions.
|
||||||
|
|
||||||
|
standard = [
|
||||||
|
"SearchBinding",
|
||||||
|
"AutoIndent",
|
||||||
|
"AutoExpand",
|
||||||
|
"ZoomHeight",
|
||||||
|
]
|
105
Tools/idle/extend.txt
Normal file
105
Tools/idle/extend.txt
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
Writing an IDLE extension
|
||||||
|
|
||||||
|
An IDLE extension can define new key bindings and menu entries for
|
||||||
|
IDLE edit windows. There is a simple mechanism to load extensions
|
||||||
|
when IDLE starts up and to attach them to each edit window.
|
||||||
|
(It is also possible to make other changes to IDLE, but this must
|
||||||
|
be done by editing the IDLE source code.)
|
||||||
|
|
||||||
|
The list of extensions loaded at startup time is configured by editing
|
||||||
|
the file extend.py; see below for details.
|
||||||
|
|
||||||
|
An IDLE extension is defined by a class. Methods of the class define
|
||||||
|
actions that are invoked by those bindings or menu entries.
|
||||||
|
Class (or instance) variables define the bindings and menu additions;
|
||||||
|
these are automatically applied by IDLE when the extension is linked
|
||||||
|
to an edit window.
|
||||||
|
|
||||||
|
An IDLE extension class is instantiated with a single argument,
|
||||||
|
`editwin', an EditorWindow instance.
|
||||||
|
The extension cannot assume much about this argument, but it
|
||||||
|
is guarateed to have the following instance variables:
|
||||||
|
|
||||||
|
text a Text instance (a widget)
|
||||||
|
io an IOBinding instance (more about this later)
|
||||||
|
flist the FileList instance (shared by all edit windows)
|
||||||
|
|
||||||
|
(There are a few more, but they are rarely useful.)
|
||||||
|
|
||||||
|
The extension class must not bind key events. Rather, it must define
|
||||||
|
one or more virtual events, e.g. <<zoom-height>>, and corresponding
|
||||||
|
methods, e.g. zoom_height(), 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(self, event):
|
||||||
|
"...Do what you want here..."
|
||||||
|
|
||||||
|
The final piece of the puzzle is the file "extend.py", which contains a
|
||||||
|
simple table used to configure the loading of extensions. This file currently
|
||||||
|
contains a single list variable named "standard", which is a list of extension
|
||||||
|
names that are to be loaded. (In the future, other configuration variables
|
||||||
|
may be added to this module.)
|
||||||
|
|
||||||
|
Extensions can define key bindings and menu entries that reference events they
|
||||||
|
don't implement (including standard events); however this is not recommended
|
||||||
|
(and may be forbidden in the future).
|
||||||
|
|
||||||
|
Extensions are not required to define menu entries for all events
|
||||||
|
they implement.
|
||||||
|
|
||||||
|
Note: in order to change key bindings, you must currently edit the file
|
||||||
|
keydefs. It contains two dictionaries named and formatted like the
|
||||||
|
keydefs dictionaries described above, one for the Unix bindings and one for
|
||||||
|
the Windows bindings. In the future, a better mechanism will be provided.
|
|
@ -1,3 +1,5 @@
|
||||||
|
[See end for tips.]
|
||||||
|
|
||||||
File menu:
|
File menu:
|
||||||
|
|
||||||
New window -- create a new editing window
|
New window -- create a new editing window
|
||||||
|
@ -75,9 +77,19 @@ Python syntax colors: the coloring is applied in a background thread
|
||||||
Comments red
|
Comments red
|
||||||
Definitions blue
|
Definitions blue
|
||||||
|
|
||||||
Console colors:
|
Shell colors:
|
||||||
|
|
||||||
Console output red
|
Console output dark red
|
||||||
stdout blue
|
stdout blue
|
||||||
stderr dark green
|
stderr dark green
|
||||||
stdin purple
|
stdin black
|
||||||
|
|
||||||
|
Tips:
|
||||||
|
To change the font on Windows, open EditorWindow.py and change
|
||||||
|
text['font'] = ("verdana", 8)
|
||||||
|
to, e.g.,
|
||||||
|
text['font'] = ("courier new", 10)
|
||||||
|
|
||||||
|
To change the Python syntax colors, edit the tagdefs table
|
||||||
|
in ColorDelegator.py; to change the shell colors, edit the
|
||||||
|
tagdefs table in PyShell.py.
|
||||||
|
|
3
Tools/idle/idle.bat
Normal file
3
Tools/idle/idle.bat
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
rem idle.bat
|
||||||
|
|
||||||
|
"C:\Program Files\Python\python.exe" "idle.pyw" %1 %2 %3 %4 %5 %6 %7 %8 %9
|
|
@ -1,3 +1,9 @@
|
||||||
#! /usr/bin/env python
|
try:
|
||||||
import PyShell
|
import PyShell
|
||||||
PyShell.main()
|
PyShell.main()
|
||||||
|
except SystemExit:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
raw_input("Hit return to exit...")
|
||||||
|
|
1
Tools/idle/idlever.py
Normal file
1
Tools/idle/idlever.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
IDLE_VERSION = "0.2"
|
59
Tools/idle/keydefs.py
Normal file
59
Tools/idle/keydefs.py
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
windows_keydefs = \
|
||||||
|
{'<<Copy>>': ['<Control-c>'],
|
||||||
|
'<<Cut>>': ['<Control-x>'],
|
||||||
|
'<<Paste>>': ['<Control-v>'],
|
||||||
|
'<<beginning-of-line>>': ['<Control-a>', '<Home>'],
|
||||||
|
'<<center-insert>>': ['<Control-l>'],
|
||||||
|
'<<close-all-windows>>': ['<Control-q>'],
|
||||||
|
'<<close-window>>': ['<Alt-F4>'],
|
||||||
|
'<<dump-undo-state>>': ['<Control-backslash>'],
|
||||||
|
'<<end-of-file>>': ['<Control-d>'],
|
||||||
|
'<<expand-word>>': ['<Alt-slash>'],
|
||||||
|
'<<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>'],
|
||||||
|
'<<redo>>': ['<Control-y>'],
|
||||||
|
'<<remove-selection>>': ['<Escape>'],
|
||||||
|
'<<save-copy-of-window-as-file>>': ['<Alt-Shift-s>'],
|
||||||
|
'<<save-window-as-file>>': ['<Alt-s>'],
|
||||||
|
'<<save-window>>': ['<Control-s>'],
|
||||||
|
'<<select-all>>': ['<Alt-a>'],
|
||||||
|
'<<toggle-auto-coloring>>': ['<Control-slash>'],
|
||||||
|
'<<undo>>': ['<Control-z>'],
|
||||||
|
}
|
||||||
|
|
||||||
|
unix_keydefs = \
|
||||||
|
{'<<Copy>>': ['<Alt-w>', '<Meta-w>'],
|
||||||
|
'<<Cut>>': ['<Control-w>'],
|
||||||
|
'<<Paste>>': ['<Control-y>'],
|
||||||
|
'<<beginning-of-line>>': ['<Control-a>', '<Home>'],
|
||||||
|
'<<center-insert>>': ['<Control-l>'],
|
||||||
|
'<<close-all-windows>>': ['<Control-x><Control-c>'],
|
||||||
|
'<<close-window>>': ['<Control-x><Control-0>', '<Control-x><Key-0>'],
|
||||||
|
'<<do-nothing>>': ['<Control-x>'],
|
||||||
|
'<<dump-undo-state>>': ['<Control-backslash>'],
|
||||||
|
'<<end-of-file>>': ['<Control-d>'],
|
||||||
|
'<<expand-word>>': ['<Alt-slash>', '<Meta-slash>'],
|
||||||
|
'<<help>>': ['<F1>'],
|
||||||
|
'<<history-next>>': ['<Alt-n>', '<Meta-n>'],
|
||||||
|
'<<history-previous>>': ['<Alt-p>', '<Meta-p>'],
|
||||||
|
'<<interrupt-execution>>': ['<Control-c>'],
|
||||||
|
'<<open-class-browser>>': ['<Control-x><Control-b>'],
|
||||||
|
'<<open-module>>': ['<Control-x><Control-m>'],
|
||||||
|
'<<open-new-window>>': ['<Control-x><Control-n>'],
|
||||||
|
'<<open-window-from-file>>': ['<Control-x><Control-f>'],
|
||||||
|
'<<plain-newline-and-indent>>': ['<Control-j>'],
|
||||||
|
'<<redo>>': ['<Alt-z>', '<Meta-z>'],
|
||||||
|
'<<save-copy-of-window-as-file>>': ['<Control-x><w>'],
|
||||||
|
'<<save-window-as-file>>': ['<Control-x><Control-w>'],
|
||||||
|
'<<save-window>>': ['<Control-x><Control-s>'],
|
||||||
|
'<<select-all>>': ['<Alt-a>', '<Meta-a>'],
|
||||||
|
'<<toggle-auto-coloring>>': ['<Control-slash>'],
|
||||||
|
'<<undo>>': ['<Control-z>'],
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue