mirror of
https://github.com/python/cpython.git
synced 2025-08-03 00:23:06 +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
|
@ -5,15 +5,70 @@ import imp
|
|||
from Tkinter import *
|
||||
import tkSimpleDialog
|
||||
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_text = """\
|
||||
IDLE 0.1
|
||||
IDLE %s
|
||||
|
||||
A not totally unintegrated development environment for Python
|
||||
An Integrated DeveLopment Environment for Python
|
||||
|
||||
by Guido van Rossum
|
||||
"""
|
||||
""" % idlever.IDLE_VERSION
|
||||
|
||||
class EditorWindow:
|
||||
|
||||
|
@ -21,44 +76,52 @@ class EditorWindow:
|
|||
from ColorDelegator import ColorDelegator
|
||||
from UndoDelegator import UndoDelegator
|
||||
from IOBinding import IOBinding
|
||||
from SearchBinding import SearchBinding
|
||||
from AutoIndent import AutoIndent
|
||||
from AutoExpand import AutoExpand
|
||||
import Bindings
|
||||
|
||||
about_title = about_title
|
||||
about_text = about_text
|
||||
from Tkinter import Toplevel
|
||||
|
||||
def __init__(self, root, filename=None):
|
||||
about_title = about_title
|
||||
about_text = about_text
|
||||
|
||||
def __init__(self, flist=None, filename=None, key=None, root=None):
|
||||
self.flist = flist
|
||||
root = root or flist.root
|
||||
self.root = 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.text = text = Text(top, name='text')
|
||||
self.text = text = Text(top, name='text', padx=5,
|
||||
background="white", wrap="none")
|
||||
|
||||
self.createmenubar()
|
||||
self.Bindings.apply_bindings(text)
|
||||
|
||||
self.top.protocol("WM_DELETE_WINDOW", self.close)
|
||||
self.top.bind("<<close-window>>", self.close_event)
|
||||
self.text.bind("<<center-insert>>", self.center_insert_event)
|
||||
self.text.bind("<<help>>", self.help_dialog)
|
||||
self.text.bind("<<about-idle>>", self.about_dialog)
|
||||
self.text.bind("<<open-module>>", self.open_module)
|
||||
self.text.bind("<<do-nothing>>", lambda event: "break")
|
||||
text.bind("<<center-insert>>", self.center_insert_event)
|
||||
text.bind("<<help>>", self.help_dialog)
|
||||
text.bind("<<about-idle>>", self.about_dialog)
|
||||
text.bind("<<open-module>>", self.open_module)
|
||||
text.bind("<<do-nothing>>", lambda event: "break")
|
||||
text.bind("<<select-all>>", self.select_all)
|
||||
text.bind("<<remove-selection>>", self.remove_selection)
|
||||
text.bind("<3>", self.right_menu_event)
|
||||
if flist:
|
||||
flist.inversedict[self] = key
|
||||
if key:
|
||||
flist.dict[key] = self
|
||||
text.bind("<<open-new-window>>", self.flist.new_callback)
|
||||
text.bind("<<close-all-windows>>", self.flist.close_all_callback)
|
||||
text.bind("<<open-class-browser>>", self.open_class_browser)
|
||||
|
||||
vbar['command'] = text.yview
|
||||
vbar.pack(side=RIGHT, fill=Y)
|
||||
|
||||
text['yscrollcommand'] = vbar.set
|
||||
text['background'] = 'white'
|
||||
if sys.platform[:3] == 'win':
|
||||
text['font'] = ("lucida console", 8)
|
||||
text.pack(side=LEFT, fill=BOTH, expand=1)
|
||||
text.focus_set()
|
||||
|
||||
self.auto = auto = self.AutoIndent(text)
|
||||
self.autoex = self.AutoExpand(text)
|
||||
self.per = per = self.Percolator(text)
|
||||
if self.ispythonsource(filename):
|
||||
self.color = color = self.ColorDelegator(); per.insertfilter(color)
|
||||
|
@ -67,8 +130,7 @@ class EditorWindow:
|
|||
##print "No initial colorizer"
|
||||
self.color = None
|
||||
self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
|
||||
self.search = search = self.SearchBinding(undo)
|
||||
self.io = io = self.IOBinding(undo)
|
||||
self.io = io = self.IOBinding(self)
|
||||
|
||||
undo.set_saved_change_hook(self.saved_change_hook)
|
||||
io.set_filename_change_hook(self.filename_change_hook)
|
||||
|
@ -81,9 +143,29 @@ class EditorWindow:
|
|||
|
||||
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 = [
|
||||
("file", "_File"),
|
||||
("edit", "_Edit"),
|
||||
("windows", "_Windows"),
|
||||
("help", "_Help"),
|
||||
]
|
||||
|
||||
|
@ -96,15 +178,78 @@ class EditorWindow:
|
|||
mbar.add_cascade(label=label, menu=menu, underline=underline)
|
||||
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):
|
||||
tkMessageBox.showinfo(self.about_title, self.about_text,
|
||||
master=self.text)
|
||||
|
||||
helpfile = "help.txt"
|
||||
|
||||
def help_dialog(self, event=None):
|
||||
from HelpWindow import HelpWindow
|
||||
HelpWindow(root=self.root)
|
||||
|
||||
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
|
||||
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):
|
||||
# XXX Shouldn't this be in IOBinding or in FileList?
|
||||
try:
|
||||
name = self.text.get("sel.first", "sel.last")
|
||||
except TclError:
|
||||
|
@ -120,6 +265,8 @@ class EditorWindow:
|
|||
name = string.strip(name)
|
||||
if not name:
|
||||
return
|
||||
# XXX Ought to support package syntax
|
||||
# XXX Ought to insert current file's directory in front of path
|
||||
try:
|
||||
(f, file, (suffix, mode, type)) = imp.find_module(name)
|
||||
except ImportError, msg:
|
||||
|
@ -131,7 +278,26 @@ class EditorWindow:
|
|||
return
|
||||
if f:
|
||||
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):
|
||||
if lineno is not None and lineno > 0:
|
||||
|
@ -143,7 +309,8 @@ class EditorWindow:
|
|||
def ispythonsource(self, filename):
|
||||
if not filename:
|
||||
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
|
||||
try:
|
||||
f = open(filename)
|
||||
|
@ -153,12 +320,16 @@ class EditorWindow:
|
|||
return 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):
|
||||
self.close_hook = close_hook
|
||||
|
||||
def filename_change_hook(self):
|
||||
if self.flist:
|
||||
self.flist.filename_changed_edit(self)
|
||||
self.saved_change_hook()
|
||||
if self.ispythonsource(self.io.filename):
|
||||
self.addcolorizer()
|
||||
|
@ -184,13 +355,40 @@ class EditorWindow:
|
|||
self.per.insertfilter(self.undo)
|
||||
|
||||
def saved_change_hook(self):
|
||||
if self.io.filename:
|
||||
title = self.io.filename
|
||||
short = self.short_title()
|
||||
long = self.long_title()
|
||||
if short and long:
|
||||
title = short + " - " + long
|
||||
elif short:
|
||||
title = short
|
||||
elif long:
|
||||
title = long
|
||||
else:
|
||||
title = "(Untitled)"
|
||||
if not self.undo.get_saved():
|
||||
title = title + " *"
|
||||
title = "Untitled"
|
||||
icon = short or long or title
|
||||
if not self.get_saved():
|
||||
title = "*%s*" % title
|
||||
icon = "*%s" % icon
|
||||
self.top.wm_title(title)
|
||||
self.top.wm_iconname(icon)
|
||||
|
||||
def get_saved(self):
|
||||
return self.undo.get_saved()
|
||||
|
||||
def set_saved(self, flag):
|
||||
self.undo.set_saved(flag)
|
||||
|
||||
def reset_undo(self):
|
||||
self.undo.reset_undo()
|
||||
|
||||
def short_title(self):
|
||||
filename = self.io.filename
|
||||
if filename:
|
||||
filename = os.path.basename(filename)
|
||||
return filename
|
||||
|
||||
def long_title(self):
|
||||
return self.io.filename or ""
|
||||
|
||||
def center_insert_event(self, event):
|
||||
self.center()
|
||||
|
@ -207,10 +405,14 @@ class EditorWindow:
|
|||
def close_event(self, event):
|
||||
self.close()
|
||||
|
||||
def maybesave(self):
|
||||
if self.io:
|
||||
return self.io.maybesave()
|
||||
|
||||
def close(self):
|
||||
self.top.wm_deiconify()
|
||||
self.top.tkraise()
|
||||
reply = self.io.maybesave()
|
||||
reply = self.maybesave()
|
||||
if reply != "cancel":
|
||||
if self.color and self.color.colorizing:
|
||||
self.color.close()
|
||||
|
@ -223,8 +425,59 @@ class EditorWindow:
|
|||
self.top.destroy()
|
||||
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):
|
||||
# Make sure that Tk's double-click and next/previous word
|
||||
# operations use our definition of a word (i.e. an identifier)
|
||||
tk = root.tk
|
||||
tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
|
||||
tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
|
||||
|
@ -239,7 +492,7 @@ def test():
|
|||
filename = sys.argv[1]
|
||||
else:
|
||||
filename = None
|
||||
edit = EditorWindow(root, filename)
|
||||
edit = EditorWindow(root=root, filename=filename)
|
||||
edit.set_close_hook(root.quit)
|
||||
root.mainloop()
|
||||
root.destroy()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue