IDLE contains an extension facility. Preferences for extensions can be
changed with Configure Extensions. See the beginning of config-extensions.def
-in the idlelib directory for further information. The default extensions
-are currently:
-
-* FormatParagraph
-
-* AutoExpand
-
-* ZoomHeight
-
-* ScriptBinding
-
-* CallTips
-
-* ParenMatch
-
-* AutoComplete
-
-* CodeContext
-
-* RstripExtension
+in the idlelib directory for further information. The only current default
+extension is zoomheight. It exists as an extension primarily to be an example
+and for testing purposes.
\ No newline at end of file
-"""autocomplete.py - An IDLE extension for automatically completing names.
+"""Complete either attribute names or file names.
-This extension can complete either attribute names or file names. It can pop
-a window with all available names, for the user to select from.
+Either on demand or after a user-selected delay after a key character,
+pop up a list of candidates.
"""
import os
import string
class AutoComplete:
- menudefs = [
- ('edit', [
- ("Show Completions", "<<force-open-completions>>"),
- ])
- ]
-
- popupwait = idleConf.GetOption("extensions", "AutoComplete",
- "popupwait", type="int", default=0)
-
def __init__(self, editwin=None):
self.editwin = editwin
- if editwin is not None: # not in subprocess or test
+ if editwin is not None: # not in subprocess or test
self.text = editwin.text
self.autocompletewindow = None
# id of delayed call, and the index of the text insert when
self._delayed_completion_id = None
self._delayed_completion_index = None
+ @classmethod
+ def reload(cls):
+ cls.popupwait = idleConf.GetOption(
+ "extensions", "AutoComplete", "popupwait", type="int", default=0)
+
def _make_autocomplete_window(self):
return autocomplete_w.AutoCompleteWindow(self.text)
return eval(name, namespace)
+AutoComplete.reload()
+
+
if __name__ == '__main__':
from unittest import main
main('idlelib.idle_test.test_autocomplete', verbosity=2)
place before requesting the next selection causes AutoExpand to reset
its state.
-This is an extension file and there is only one instance of AutoExpand.
+There is only one instance of Autoexpand.
'''
import re
import string
-###$ event <<expand-word>>
-###$ win <Alt-slash>
-###$ unix <Alt-slash>
class AutoExpand:
-
- menudefs = [
- ('edit', [
- ('E_xpand Word', '<<expand-word>>'),
- ]),
- ]
-
wordchars = string.ascii_letters + string.digits + "_"
def __init__(self, editwin):
i = i-1
return line[i:]
+
if __name__ == '__main__':
import unittest
unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2)
-"""calltips.py - An IDLE Extension to Jog Your Memory
+"""Pop up a reminder of how to call a function.
Call Tips are floating windows which display function, class, and method
parameter and docstring information when you type an opening parenthesis, and
which disappear when you type a closing parenthesis.
-
"""
import inspect
import re
from idlelib.hyperparser import HyperParser
import __main__
-class CallTips:
- menudefs = [
- ('edit', [
- ("Show call tip", "<<force-open-calltip>>"),
- ])
- ]
+class CallTips:
def __init__(self, editwin=None):
if editwin is None: # subprocess and test
else:
return get_argspec(get_entity(expression))
+
def get_entity(expression):
"""Return the object corresponding to expression evaluated
in a namespace spanning sys.modules and __main.dict__.
_invalid_method = "invalid method signature"
_argument_positional = "\n['/' marks preceding arguments as positional-only]\n"
-
def get_argspec(ob):
'''Return a string describing the signature of a callable object, or ''.
-"""codecontext - Extension to display the block context above the edit window
+"""codecontext - display the block context above the edit window
Once code has scrolled off the top of a window, it can be difficult to
determine which block you are in. This extension implements a pane at the top
getspacesfirstword =\
lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups()
+
class CodeContext:
- menudefs = [('options', [('!Code Conte_xt', '<<toggle-code-context>>')])]
- context_depth = idleConf.GetOption("extensions", "CodeContext",
- "numlines", type="int", default=3)
bgcolor = idleConf.GetOption("extensions", "CodeContext",
"bgcolor", type="str", default="LightGray")
fgcolor = idleConf.GetOption("extensions", "CodeContext",
# starts the toplevel 'block' of the module.
self.info = [(0, -1, "", False)]
self.topvisible = 1
- visible = idleConf.GetOption("extensions", "CodeContext",
- "visible", type="bool", default=False)
- if visible:
- self.toggle_code_context_event()
- self.editwin.setvar('<<toggle-code-context>>', True)
+ self.reload()
# Start two update cycles, one for context lines, one for font changes.
self.text.after(UPDATEINTERVAL, self.timer_event)
self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
+ @classmethod
+ def reload(cls):
+ cls.context_depth = idleConf.GetOption("extensions", "CodeContext",
+ "numlines", type="int", default=3)
+ cls.bgcolor = idleConf.GetOption("extensions", "CodeContext",
+ "bgcolor", type="str", default="LightGray")
+ cls.fgcolor = idleConf.GetOption("extensions", "CodeContext",
+ "fgcolor", type="str", default="Black")
+
def toggle_code_context_event(self, event=None):
if not self.label:
# Calculate the border width and horizontal padding required to
else:
self.label.destroy()
self.label = None
- idleConf.SetOption("extensions", "CodeContext", "visible",
+ idleConf.SetOption("main", "Theme", "contexton",
str(self.label is not None))
idleConf.SaveUserCfgFiles()
return "break"
self.textfont = newtextfont
self.label["font"] = self.textfont
self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
+
+
+CodeContext.reload()
# config-extensions.def
#
+# The following sections are for features that are no longer extensions.
+# Their options values are left here for back-compatibility.
+
+[AutoComplete]
+popupwait= 2000
+
+[CodeContext]
+numlines= 3
+visible= False
+bgcolor= LightGray
+fgcolor= Black
+
+[FormatParagraph]
+max-width= 72
+
+[ParenMatch]
+style= expression
+flash-delay= 500
+bell= True
+
# IDLE reads several config files to determine user preferences. This
# file is the default configuration file for IDLE extensions settings.
#
# extension that may be sensibly re-configured.
#
# If there are no keybindings for a menus' virtual events, include lines
-# like <<toggle-code-context>>= (See [CodeContext], below.)
+# like <<toggle-code-context>>=.
#
# Currently it is necessary to manually modify this file to change
# extension key bindings and default values. To customize, create
# See config-keys.def for notes on specifying keys and extend.txt for
# information on creating IDLE extensions.
-[AutoComplete]
-enable=True
-popupwait=2000
-[AutoComplete_cfgBindings]
-force-open-completions=<Control-Key-space>
-[AutoComplete_bindings]
-autocomplete=<Key-Tab>
-try-open-completions=<KeyRelease-period> <KeyRelease-slash> <KeyRelease-backslash>
-
-[AutoExpand]
-enable=True
-[AutoExpand_cfgBindings]
-expand-word=<Alt-Key-slash>
-
-[CallTips]
-enable=True
-[CallTips_cfgBindings]
-force-open-calltip=<Control-Key-backslash>
-[CallTips_bindings]
-try-open-calltip=<KeyRelease-parenleft>
-refresh-calltip=<KeyRelease-parenright> <KeyRelease-0>
-
-[CodeContext]
-enable=True
-enable_shell=False
-numlines=3
-visible=False
-bgcolor=LightGray
-fgcolor=Black
-[CodeContext_bindings]
-toggle-code-context=
-
-[FormatParagraph]
-enable=True
-max-width=72
-[FormatParagraph_cfgBindings]
-format-paragraph=<Alt-Key-q>
-
-[ParenMatch]
-enable=True
-style= expression
-flash-delay= 500
-bell=True
-[ParenMatch_cfgBindings]
-flash-paren=<Control-Key-0>
-[ParenMatch_bindings]
-paren-closed=<KeyRelease-parenright> <KeyRelease-bracketright> <KeyRelease-braceright>
-
-[RstripExtension]
-enable=True
-enable_shell=False
-enable_editor=True
-
-[ScriptBinding]
-enable=True
-enable_shell=False
-enable_editor=True
-[ScriptBinding_cfgBindings]
-run-module=<Key-F5>
-check-module=<Alt-Key-x>
-
-[ZoomHeight]
-enable=True
-[ZoomHeight_cfgBindings]
-zoom-height=<Alt-Key-2>
+# A fake extension for testing and example purposes. When enabled and
+# invoked, inserts or deletes z-text at beginning of every line.
+[ZzDummy]
+enable= True
+enable_shell = False
+enable_editor = True
+z-text= Z
+[ZzDummy_cfgBindings]
+z-in= <Control-Shift-KeyRelease-Insert>
+[ZzDummy_bindings]
+z-out= <Control-Shift-KeyRelease-Delete>
change-indentwidth=<Alt-Key-u> <Meta-Key-u> <Alt-Key-U> <Meta-Key-U>
del-word-left=<Control-Key-BackSpace>
del-word-right=<Control-Key-Delete>
+force-open-completions= <Control-Key-space>
+expand-word= <Alt-Key-slash>
+force-open-calltip= <Control-Key-backslash>
+format-paragraph= <Alt-Key-q>
+flash-paren= <Control-Key-0>
+run-module= <Key-F5>
+check-module= <Alt-Key-x>
+zoom-height= <Alt-Key-2>
[IDLE Classic Unix]
copy=<Alt-Key-w> <Meta-Key-w>
change-indentwidth=<Alt-Key-u>
del-word-left=<Alt-Key-BackSpace>
del-word-right=<Alt-Key-d>
+force-open-completions= <Control-Key-space>
+expand-word= <Alt-Key-slash>
+force-open-calltip= <Control-Key-backslash>
+format-paragraph= <Alt-Key-q>
+flash-paren= <Control-Key-0>
+run-module= <Key-F5>
+check-module= <Alt-Key-x>
+zoom-height= <Alt-Key-2>
[IDLE Modern Unix]
copy = <Control-Shift-Key-C> <Control-Key-Insert>
change-indentwidth = <Alt-Key-u>
del-word-left = <Control-Key-BackSpace>
del-word-right = <Control-Key-Delete>
+force-open-completions= <Control-Key-space>
+expand-word= <Alt-Key-slash>
+force-open-calltip= <Control-Key-backslash>
+format-paragraph= <Alt-Key-q>
+flash-paren= <Control-Key-0>
+run-module= <Key-F5>
+check-module= <Alt-Key-x>
+zoom-height= <Alt-Key-2>
[IDLE Classic Mac]
copy=<Command-Key-c>
change-indentwidth=<Control-Key-u>
del-word-left=<Control-Key-BackSpace>
del-word-right=<Control-Key-Delete>
+force-open-completions= <Control-Key-space>
+expand-word= <Option-Key-slash>
+force-open-calltip= <Control-Key-backslash>
+format-paragraph= <Option-Key-q>
+flash-paren= <Control-Key-0>
+run-module= <Key-F5>
+check-module= <Option-Key-x>
+zoom-height= <Option-Key-0>
[IDLE Classic OSX]
toggle-tabs = <Control-Key-t>
save-copy-of-window-as-file = <Option-Command-Key-s>
open-window-from-file = <Command-Key-o>
python-docs = <Key-F1>
-
+force-open-completions= <Control-Key-space>
+expand-word= <Option-Key-slash>
+force-open-calltip= <Control-Key-backslash>
+format-paragraph= <Option-Key-q>
+flash-paren= <Control-Key-0>
+run-module= <Key-F5>
+check-module= <Option-Key-x>
+zoom-height= <Option-Key-0>
'stderr-foreground':'#000000',
'stderr-background':'#ffffff',
'console-foreground':'#000000',
- 'console-background':'#ffffff' }
+ 'console-background':'#ffffff',
+ }
for element in theme:
if not cfgParser.has_option(themeName, element):
# Print warning that will return a default color
for extn in userExtns:
if extn not in extns: #user has added own extension
extns.append(extn)
+ for extn in ('AutoComplete','CodeContext',
+ 'FormatParagraph','ParenMatch'):
+ extns.remove(extn)
+ # specific exclusions because we are storing config for mainlined old
+ # extensions in config-extensions.def for backward compatibility
if active_only:
activeExtns = []
for extn in extns:
return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
# TODO make keyBindins a file or class attribute used for test above
-# and copied in function below
+# and copied in function below.
+
+ former_extension_events = { # Those with user-configurable keys.
+ '<<force-open-completions>>', '<<expand-word>>',
+ '<<force-open-calltip>>', '<<flash-paren>>', '<<format-paragraph>>',
+ '<<run-module>>', '<<check-module>>', '<<zoom-height>>'}
def GetCoreKeys(self, keySetName=None):
"""Return dict of core virtual-key keybindings for keySetName.
'<<toggle-tabs>>': ['<Alt-Key-t>'],
'<<change-indentwidth>>': ['<Alt-Key-u>'],
'<<del-word-left>>': ['<Control-Key-BackSpace>'],
- '<<del-word-right>>': ['<Control-Key-Delete>']
+ '<<del-word-right>>': ['<Control-Key-Delete>'],
+ '<<force-open-completions>>': ['<Control-Key-space>'],
+ '<<expand-word>>': ['<Alt-Key-slash>'],
+ '<<force-open-calltip>>': ['<Control-Key-backslash>'],
+ '<<flash-paren>>': ['<Control-Key-0>'],
+ '<<format-paragraph>>': ['<Alt-Key-q>'],
+ '<<run-module>>': ['<Key-F5>'],
+ '<<check-module>>': ['<Alt-Key-x>'],
+ '<<zoom-height>>': ['<Alt-Key-2>'],
}
+
if keySetName:
if not (self.userCfg['keys'].has_section(keySetName) or
self.defaultCfg['keys'].has_section(keySetName)):
binding = self.GetKeyBinding(keySetName, event)
if binding:
keyBindings[event] = binding
- else: #we are going to return a default, print warning
+ # Otherwise return default in keyBindings.
+ elif event not in self.former_extension_events:
warning = (
'\n Warning: config.py - IdleConf.GetCoreKeys -\n'
' problem retrieving key binding for event %r\n'
NONE, BOTH, X, Y, W, E, EW, NS, NSEW, NW,
HORIZONTAL, VERTICAL, ANCHOR, ACTIVE, END)
from tkinter.ttk import (Button, Checkbutton, Entry, Frame, Label, LabelFrame,
- Notebook, Radiobutton, Scrollbar, Style)
+ OptionMenu, Notebook, Radiobutton, Scrollbar, Style)
import tkinter.colorchooser as tkColorChooser
import tkinter.font as tkFont
from tkinter import messagebox
def deactivate_current_config(self):
"""Remove current key bindings.
-
Iterate over window instances defined in parent and remove
the keybindings.
"""
"Fill self.extensions with data from the default and user configs."
self.extensions = {}
for ext_name in idleConf.GetExtensions(active_only=False):
+ # Former built-in extensions are already filtered out.
self.extensions[ext_name] = []
for ext_name in self.extensions:
takefocus=FALSE, highlightthickness=0, wrap=NONE)
text.bind('<Double-Button-1>', lambda e: 'break')
text.bind('<B1-Motion>', lambda e: 'break')
- text_and_tags=(('\n', 'normal'),
+ text_and_tags=(
+ ('\n', 'normal'),
('#you can click here', 'comment'), ('\n', 'normal'),
('#to choose items', 'comment'), ('\n', 'normal'),
('def', 'keyword'), (' ', 'normal'),
frame_theme, text='Delete Custom Theme',
command=self.delete_custom)
self.theme_message = Label(frame_theme, borderwidth=2)
-
# Pack widgets:
# body.
frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
- frame_theme.pack(side=LEFT, padx=5, pady=5, fill=Y)
+ frame_theme.pack(side=TOP, padx=5, pady=5, fill=X)
# frame_custom.
self.frame_color_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0)
(*)helplist: ListBox
scroll_helplist: Scrollbar
"""
+ # Integer values need StringVar because int('') raises.
self.startup_edit = tracers.add(
IntVar(self), ('main', 'General', 'editor-on-startup'))
- self.autosave = tracers.add(
- IntVar(self), ('main', 'General', 'autosave'))
self.win_width = tracers.add(
StringVar(self), ('main', 'EditorWindow', 'width'))
self.win_height = tracers.add(
StringVar(self), ('main', 'EditorWindow', 'height'))
+ self.autocomplete_wait = tracers.add(
+ StringVar(self), ('extensions', 'AutoComplete', 'popupwait'))
+ self.paren_style = tracers.add(
+ StringVar(self), ('extensions', 'ParenMatch', 'style'))
+ self.flash_delay = tracers.add(
+ StringVar(self), ('extensions', 'ParenMatch', 'flash-delay'))
+ self.paren_bell = tracers.add(
+ BooleanVar(self), ('extensions', 'ParenMatch', 'bell'))
+
+ self.autosave = tracers.add(
+ IntVar(self), ('main', 'General', 'autosave'))
+ self.format_width = tracers.add(
+ StringVar(self), ('extensions', 'FormatParagraph', 'max-width'))
+ self.context_lines = tracers.add(
+ StringVar(self), ('extensions', 'CodeContext', 'numlines'))
+ # Create widgets:
# Section frames.
frame_window = LabelFrame(self, borderwidth=2, relief=GROOVE,
text=' Window Preferences')
frame_run, variable=self.startup_edit, value=0,
text='Open Shell Window')
- frame_win_size = Frame(frame_window, borderwidth=0,)
+ frame_win_size = Frame(frame_window, borderwidth=0)
win_size_title = Label(
frame_win_size, text='Initial Window Size (in characters)')
win_width_title = Label(frame_win_size, text='Width')
self.win_height_int = Entry(
frame_win_size, textvariable=self.win_height, width=3)
+ frame_autocomplete = Frame(frame_window, borderwidth=0,)
+ auto_wait_title = Label(frame_autocomplete,
+ text='Completions Popup Wait (milliseconds)')
+ self.auto_wait_int = Entry(frame_autocomplete, width=6,
+ textvariable=self.autocomplete_wait)
+
+ frame_paren1 = Frame(frame_window, borderwidth=0)
+ paren_style_title = Label(frame_paren1, text='Paren Match Style')
+ self.paren_style_type = OptionMenu(
+ frame_paren1, self.paren_style, 'expression',
+ "opener","parens","expression")
+ frame_paren2 = Frame(frame_window, borderwidth=0)
+ paren_time_title = Label(
+ frame_paren2, text='Time Match Displayed (milliseconds)\n'
+ '(0 is until next input)')
+ self.paren_flash_time = Entry(
+ frame_paren2, textvariable=self.flash_delay, width=6)
+ self.bell_on = Checkbutton(
+ frame_paren2, text="Bell on Mismatch", variable=self.paren_bell)
+
# Frame_editor.
frame_save = Frame(frame_editor, borderwidth=0)
run_save_title = Label(frame_save, text='At Start of Run (F5) ')
frame_save, variable=self.autosave, value=1,
text='No Prompt')
+ frame_format = Frame(frame_editor, borderwidth=0)
+ format_width_title = Label(frame_format,
+ text='Format Paragraph Max Width')
+ self.format_width_int = Entry(
+ frame_format, textvariable=self.format_width, width=4)
+
+ frame_context = Frame(frame_editor, borderwidth=0)
+ context_title = Label(frame_context, text='Context Lines :')
+ self.context_int = Entry(
+ frame_context, textvariable=self.context_lines, width=3)
+
+
# frame_help.
frame_helplist = Frame(frame_help)
frame_helplist_buttons = Frame(frame_helplist)
win_height_title.pack(side=RIGHT, anchor=E, pady=5)
self.win_width_int.pack(side=RIGHT, anchor=E, padx=10, pady=5)
win_width_title.pack(side=RIGHT, anchor=E, pady=5)
+ # frame_autocomplete.
+ frame_autocomplete.pack(side=TOP, padx=5, pady=0, fill=X)
+ auto_wait_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
+ self.auto_wait_int.pack(side=TOP, padx=10, pady=5)
+ # frame_paren.
+ frame_paren1.pack(side=TOP, padx=5, pady=0, fill=X)
+ paren_style_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
+ self.paren_style_type.pack(side=TOP, padx=10, pady=5)
+ frame_paren2.pack(side=TOP, padx=5, pady=0, fill=X)
+ paren_time_title.pack(side=LEFT, anchor=W, padx=5)
+ self.bell_on.pack(side=RIGHT, anchor=E, padx=15, pady=5)
+ self.paren_flash_time.pack(side=TOP, anchor=W, padx=15, pady=5)
+
# frame_save.
frame_save.pack(side=TOP, padx=5, pady=0, fill=X)
run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
self.save_auto_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
self.save_ask_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
+ # frame_format.
+ frame_format.pack(side=TOP, padx=5, pady=0, fill=X)
+ format_width_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
+ self.format_width_int.pack(side=TOP, padx=10, pady=5)
+ # frame_context.
+ frame_context.pack(side=TOP, padx=5, pady=0, fill=X)
+ context_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
+ self.context_int.pack(side=TOP, padx=5, pady=5)
+
# frame_help.
frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
def load_general_cfg(self):
"Load current configuration settings for the general options."
- # Set startup state.
+ # Set variables for all windows.
self.startup_edit.set(idleConf.GetOption(
- 'main', 'General', 'editor-on-startup', default=0, type='bool'))
- # Set autosave state.
- self.autosave.set(idleConf.GetOption(
- 'main', 'General', 'autosave', default=0, type='bool'))
- # Set initial window size.
+ 'main', 'General', 'editor-on-startup', type='bool'))
self.win_width.set(idleConf.GetOption(
'main', 'EditorWindow', 'width', type='int'))
self.win_height.set(idleConf.GetOption(
'main', 'EditorWindow', 'height', type='int'))
+ self.autocomplete_wait.set(idleConf.GetOption(
+ 'extensions', 'AutoComplete', 'popupwait', type='int'))
+ self.paren_style.set(idleConf.GetOption(
+ 'extensions', 'ParenMatch', 'style'))
+ self.flash_delay.set(idleConf.GetOption(
+ 'extensions', 'ParenMatch', 'flash-delay', type='int'))
+ self.paren_bell.set(idleConf.GetOption(
+ 'extensions', 'ParenMatch', 'bell'))
+
+ # Set variables for editor windows.
+ self.autosave.set(idleConf.GetOption(
+ 'main', 'General', 'autosave', default=0, type='bool'))
+ self.format_width.set(idleConf.GetOption(
+ 'extensions', 'FormatParagraph', 'max-width', type='int'))
+ self.context_lines.set(idleConf.GetOption(
+ 'extensions', 'CodeContext', 'numlines', type='int'))
+
# Set additional help sources.
self.user_helplist = idleConf.GetAllExtraHelpSourcesList()
self.helplist.delete(0, 'end')
be used with older IDLE releases if it is saved as a custom
key set, with a different name.
''',
- 'Extensions': '''
-Extensions:
+ 'General': '''
+General:
-Autocomplete: Popupwait is milleseconds to wait after key char, without
+AutoComplete: Popupwait is milleseconds to wait after key char, without
cursor movement, before popping up completion box. Key char is '.' after
identifier or a '/' (or '\\' on Windows) within a string.
TK_TABWIDTH_DEFAULT = 8
_py_version = ' (%s)' % platform.python_version()
-
def _sphinx_version():
"Format sys.version_info to produce the Sphinx version string used to install the chm docs"
major, minor, micro, level, serial = sys.version_info
from idlelib import mainmenu
from tkinter import Toplevel
from idlelib.statusbar import MultiStatusBar
+ from idlelib.autocomplete import AutoComplete
+ from idlelib.autoexpand import AutoExpand
+ from idlelib.calltips import CallTips
+ from idlelib.codecontext import CodeContext
+ from idlelib.paragraph import FormatParagraph
+ from idlelib.parenmatch import ParenMatch
+ from idlelib.rstrip import RstripExtension
+ from idlelib.zoomheight import ZoomHeight
filesystemencoding = sys.getfilesystemencoding() # for file names
help_url = None
def __init__(self, flist=None, filename=None, key=None, root=None):
+ # Delay import: runscript imports pyshell imports EditorWindow.
+ from idlelib.runscript import ScriptBinding
+
if EditorWindow.help_url is None:
dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html')
if sys.platform.count('linux'):
# Safari requires real file:-URLs
EditorWindow.help_url = 'file://' + EditorWindow.help_url
else:
- EditorWindow.help_url = "https://docs.python.org/%d.%d/" % sys.version_info[:2]
+ EditorWindow.help_url = ("https://docs.python.org/%d.%d/"
+ % sys.version_info[:2])
self.flist = flist
root = root or flist.root
self.root = root
self.askinteger = tkSimpleDialog.askinteger
self.showerror = tkMessageBox.showerror
+ # Add pseudoevents for former extension fixed keys.
+ # (This probably needs to be done once in the process.)
+ text.event_add('<<autocomplete>>', '<Key-Tab>')
+ text.event_add('<<try-open-completions>>', '<KeyRelease-period>',
+ '<KeyRelease-slash>', '<KeyRelease-backslash>')
+ text.event_add('<<try-open-calltip>>', '<KeyRelease-parenleft>')
+ text.event_add('<<refresh-calltip>>', '<KeyRelease-parenright>')
+ text.event_add('<<paren-closed>>', '<KeyRelease-parenright>',
+ '<KeyRelease-bracketright>', '<KeyRelease-braceright>')
+
+ # Former extension bindings depends on frame.text being packed
+ # (called from self.ResetColorizer()).
+ autocomplete = self.AutoComplete(self)
+ text.bind("<<autocomplete>>", autocomplete.autocomplete_event)
+ text.bind("<<try-open-completions>>",
+ autocomplete.try_open_completions_event)
+ text.bind("<<force-open-completions>>",
+ autocomplete.force_open_completions_event)
+ text.bind("<<expand-word>>", self.AutoExpand(self).expand_word_event)
+ text.bind("<<format-paragraph>>",
+ self.FormatParagraph(self).format_paragraph_event)
+ parenmatch = self.ParenMatch(self)
+ text.bind("<<flash-paren>>", parenmatch.flash_paren_event)
+ text.bind("<<paren-closed>>", parenmatch.paren_closed_event)
+ scriptbinding = ScriptBinding(self)
+ text.bind("<<check-module>>", scriptbinding.check_module_event)
+ text.bind("<<run-module>>", scriptbinding.run_module_event)
+ text.bind("<<do-rstrip>>", self.RstripExtension(self).do_rstrip)
+ calltips = self.CallTips(self)
+ text.bind("<<try-open-calltip>>", calltips.try_open_calltip_event)
+ #refresh-calltips must come after paren-closed to work right
+ text.bind("<<refresh-calltip>>", calltips.refresh_calltip_event)
+ text.bind("<<force-open-calltip>>", calltips.force_open_calltip_event)
+ text.bind("<<zoom-height>>", self.ZoomHeight(self).zoom_height_event)
+ text.bind("<<toggle-code-context>>",
+ self.CodeContext(self).toggle_code_context_event)
+
def _filename_to_unicode(self, filename):
"""Return filename as BMP unicode so diplayable in Tk."""
# Decode bytes to unicode.
def get_standard_extension_names(self):
return idleConf.GetExtensions(editor_only=True)
- extfiles = { # map config-extension section names to new file names
- 'AutoComplete': 'autocomplete',
- 'AutoExpand': 'autoexpand',
- 'CallTips': 'calltips',
- 'CodeContext': 'codecontext',
- 'FormatParagraph': 'paragraph',
- 'ParenMatch': 'parenmatch',
- 'RstripExtension': 'rstrip',
- 'ScriptBinding': 'runscript',
- 'ZoomHeight': 'zoomheight',
+ extfiles = { # Map built-in config-extension section names to file names.
+ 'ZzDummy': 'zzdummy',
}
def load_extension(self, name):
eq = self.assertEqual
eq(conf.GetExtensions(),
- ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext',
- 'FormatParagraph', 'ParenMatch', 'RstripExtension', 'ScriptBinding',
- 'ZoomHeight'])
+ ['ZzDummy'])
eq(conf.GetExtensions(active_only=False),
- ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext',
- 'FormatParagraph', 'ParenMatch', 'RstripExtension', 'ScriptBinding',
- 'ZoomHeight', 'DISABLE'])
+ ['ZzDummy', 'DISABLE'])
eq(conf.GetExtensions(editor_only=True),
- ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext',
- 'FormatParagraph', 'ParenMatch', 'RstripExtension', 'ScriptBinding',
- 'ZoomHeight'])
+ ['ZzDummy'])
eq(conf.GetExtensions(shell_only=True),
- ['AutoComplete', 'AutoExpand', 'CallTips', 'FormatParagraph',
- 'ParenMatch', 'ZoomHeight'])
+ [])
eq(conf.GetExtensions(active_only=False, editor_only=True),
- ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext',
- 'FormatParagraph', 'ParenMatch', 'RstripExtension',
- 'ScriptBinding', 'ZoomHeight', 'DISABLE'])
- eq(conf.GetExtensions(active_only=False, shell_only=True),
- ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext',
- 'FormatParagraph', 'ParenMatch', 'RstripExtension', 'ScriptBinding',
- 'ZoomHeight', 'DISABLE'])
+ ['ZzDummy', 'DISABLE'])
# Add user extensions
conf.SetOption('extensions', 'Foobar', 'enable', 'True')
eq(conf.GetExtensions(),
- ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext',
- 'FormatParagraph', 'ParenMatch', 'RstripExtension',
- 'ScriptBinding', 'ZoomHeight', 'Foobar']) # User extensions didn't sort
+ ['ZzDummy', 'Foobar']) # User extensions didn't sort
eq(conf.GetExtensions(active_only=False),
- ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext',
- 'FormatParagraph', 'ParenMatch', 'RstripExtension',
- 'ScriptBinding', 'ZoomHeight', 'DISABLE', 'Foobar'])
+ ['ZzDummy', 'DISABLE', 'Foobar'])
def test_remove_key_bind_names(self):
conf = self.mock_config()
self.assertCountEqual(
conf.RemoveKeyBindNames(conf.GetSectionList('default', 'extensions')),
- ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext',
- 'FormatParagraph', 'ParenMatch', 'RstripExtension', 'ScriptBinding',
- 'ZoomHeight'])
+ ['AutoComplete', 'CodeContext', 'FormatParagraph', 'ParenMatch','ZzDummy'])
def test_get_extn_name_for_event(self):
conf = self.mock_config()
eq = self.assertEqual
- eq(conf.GetExtnNameForEvent('force-open-completions'), 'AutoComplete')
- eq(conf.GetExtnNameForEvent('expand-word'), 'AutoExpand')
- eq(conf.GetExtnNameForEvent('force-open-calltip'), 'CallTips')
- eq(conf.GetExtnNameForEvent('zoom-height'), 'ZoomHeight')
+ eq(conf.GetExtnNameForEvent('z-in'), 'ZzDummy')
+ eq(conf.GetExtnNameForEvent('z-out'), None)
def test_get_extension_keys(self):
conf = self.mock_config()
eq = self.assertEqual
- eq(conf.GetExtensionKeys('AutoComplete'),
- {'<<force-open-completions>>': ['<Control-Key-space>']})
- eq(conf.GetExtensionKeys('ParenMatch'),
- {'<<flash-paren>>': ['<Control-Key-0>']})
-
- key = ['<Option-Key-2>'] if sys.platform == 'darwin' else ['<Alt-Key-2>']
- eq(conf.GetExtensionKeys('ZoomHeight'), {'<<zoom-height>>': key})
+ eq(conf.GetExtensionKeys('ZzDummy'),
+ {'<<z-in>>': ['<Control-Shift-KeyRelease-Insert>']})
+# need option key test
+## key = ['<Option-Key-2>'] if sys.platform == 'darwin' else ['<Alt-Key-2>']
+## eq(conf.GetExtensionKeys('ZoomHeight'), {'<<zoom-height>>': key})
def test_get_extension_bindings(self):
conf = self.mock_config()
self.assertEqual(conf.GetExtensionBindings('NotExists'), {})
- key = ['<Option-Key-2>'] if sys.platform == 'darwin' else ['<Alt-Key-2>']
+ #key = ['<Option-Key-2>'] if sys.platform == 'darwin' else ['<Alt-Key-2>']
+ expect = {'<<z-in>>': ['<Control-Shift-KeyRelease-Insert>'],
+ '<<z-out>>': ['<Control-Shift-KeyRelease-Delete>']}
self.assertEqual(
- conf.GetExtensionBindings('ZoomHeight'), {'<<zoom-height>>': key})
+ conf.GetExtensionBindings('ZzDummy'), expect)
# Add non-configuarable bindings
conf.defaultCfg['extensions'].add_section('Foobar')
sys.platform = 'some-linux'
self.assertEqual(conf.GetCurrentKeySet(), conf.GetKeySet(conf.CurrentKeys()))
- # This should not be the same, sicne replace <Alt- to <Option-
- sys.platform = 'darwin'
- self.assertNotEqual(conf.GetCurrentKeySet(), conf.GetKeySet(conf.CurrentKeys()))
+ # This should not be the same, since replace <Alt- to <Option-.
+ # Above depended on config-extensions.def having Alt keys,
+ # which is no longer true.
+ # sys.platform = 'darwin'
+ # self.assertNotEqual(conf.GetCurrentKeySet(), conf.GetKeySet(conf.CurrentKeys()))
# Restore platform
sys.platform = current_platform
d.custom_name.set('my custom keys')
d.bindingslist.delete(0, 'end')
d.bindingslist.insert(0, 'copy')
- d.bindingslist.insert(1, 'expand-word')
+ d.bindingslist.insert(1, 'z-in')
d.bindingslist.selection_set(0)
d.bindingslist.selection_anchor(0)
# Core binding - adds to keys.
d.keybinding.set('<Key-F11>')
self.assertEqual(keyspage,
{'my custom keys': {'copy': '<Key-F11>'}})
+
# Not a core binding - adds to extensions.
d.bindingslist.selection_set(1)
d.bindingslist.selection_anchor(1)
d.keybinding.set('<Key-F11>')
self.assertEqual(extpage,
- {'AutoExpand_cfgBindings': {'expand-word': '<Key-F11>'}})
+ {'ZzDummy_cfgBindings': {'z-in': '<Key-F11>'}})
def test_set_keys_type(self):
eq = self.assertEqual
self.assertEqual(mainpage,
{'General': {'editor-on-startup': '0'}})
- def test_autosave(self):
- d = self.page
- d.save_auto_on.invoke()
- self.assertEqual(mainpage, {'General': {'autosave': '1'}})
- d.save_ask_on.invoke()
- self.assertEqual(mainpage, {'General': {'autosave': '0'}})
-
def test_editor_size(self):
d = self.page
d.win_height_int.insert(0, '1')
d.win_width_int.insert(0, '1')
self.assertEqual(mainpage, {'EditorWindow': {'width': '180'}})
+ def test_autocomplete_wait(self):
+ self.page.auto_wait_int.insert(0, '1')
+ self.assertEqual(extpage, {'AutoComplete': {'popupwait': '12000'}})
+
+ def test_parenmatch(self):
+ d = self.page
+ eq = self.assertEqual
+ d.paren_style_type['menu'].invoke(0)
+ eq(extpage, {'ParenMatch': {'style': 'opener'}})
+ changes.clear()
+ d.paren_flash_time.insert(0, '2')
+ eq(extpage, {'ParenMatch': {'flash-delay': '2500'}})
+ changes.clear()
+ d.bell_on.invoke()
+ eq(extpage, {'ParenMatch': {'bell': 'False'}})
+
+ def test_autosave(self):
+ d = self.page
+ d.save_auto_on.invoke()
+ self.assertEqual(mainpage, {'General': {'autosave': '1'}})
+ d.save_ask_on.invoke()
+ self.assertEqual(mainpage, {'General': {'autosave': '0'}})
+
+ def test_paragraph(self):
+ self.page.format_width_int.insert(0, '1')
+ self.assertEqual(extpage, {'FormatParagraph': {'max-width': '172'}})
+
+ def test_context(self):
+ self.page.context_int.insert(0, '1')
+ self.assertEqual(extpage, {'CodeContext': {'numlines': '13'}})
+
def test_source_selected(self):
d = self.page
d.set = d.set_add_delete_state
('Find in Files...', '<<find-in-files>>'),
('R_eplace...', '<<replace>>'),
('Go to _Line', '<<goto-line>>'),
+ ('S_how Completions', '<<force-open-completions>>'),
+ ('E_xpand Word', '<<expand-word>>'),
+ ('Show C_all Tip', '<<force-open-calltip>>'),
+ ('Show Surrounding P_arens', '<<flash-paren>>'),
+
]),
('format', [
('_Indent Region', '<<indent-region>>'),
('Untabify Region', '<<untabify-region>>'),
('Toggle Tabs', '<<toggle-tabs>>'),
('New Indent Width', '<<change-indentwidth>>'),
+ ('F_ormat Paragraph', '<<format-paragraph>>'),
+ ('S_trip Trailing Whitespace', '<<do-rstrip>>'),
]),
('run', [
('Python Shell', '<<open-python-shell>>'),
+ ('C_heck Module', '<<check-module>>'),
+ ('R_un Module', '<<run-module>>'),
]),
('shell', [
('_View Last Restart', '<<view-restart>>'),
]),
('options', [
('Configure _IDLE', '<<open-config-dialog>>'),
- None,
+ ('_Code Context', '<<toggle-code-context>>'),
+ ]),
+ ('windows', [
+ ('Zoom Height', '<<zoom-height>>'),
]),
('help', [
('_About IDLE', '<<about-idle>>'),
def __init__(self, *args):
EditorWindow.__init__(self, *args)
self.text.bind("<<goto-file-line>>", self.goto_file_line)
+ self.text.unbind("<<toggle-code-context>>")
# Customize EditorWindow
def ispythonsource(self, filename):
-"""Extension to format a paragraph or selection to a max width.
+"""Format a paragraph, comment block, or selection to a max width.
Does basic, standard text formatting, and also understands Python
comment blocks. Thus, for editing Python source code, this
class FormatParagraph:
- menudefs = [
- ('format', [ # /s/edit/format dscherer@cmu.edu
- ('Format Paragraph', '<<format-paragraph>>'),
- ])
- ]
-
def __init__(self, editwin):
self.editwin = editwin
+ @classmethod
+ def reload(cls):
+ cls.max_width = idleConf.GetOption('extensions', 'FormatParagraph',
+ 'max-width', type='int', default=72)
+
def close(self):
self.editwin = None
The length limit parameter is for testing with a known value.
"""
- if limit is None:
- # The default length limit is that defined by pep8
- limit = idleConf.GetOption(
- 'extensions', 'FormatParagraph', 'max-width',
- type='int', default=72)
+ limit = self.max_width if limit is None else limit
text = self.editwin.text
first, last = self.editwin.get_selection_indices()
if first and last:
text.see("insert")
return "break"
+
+FormatParagraph.reload()
+
def find_paragraph(text, mark):
"""Returns the start/stop indices enclosing the paragraph that mark is in.
-"""ParenMatch -- An IDLE extension for parenthesis matching.
+"""ParenMatch -- for parenthesis matching.
When you hit a right paren, the cursor should move briefly to the left
paren. Paren here is used generically; the matching applies to
- Highlight when cursor is moved to the right of a closer.
This might be too expensive to check.
"""
- menudefs = [
- ('edit', [
- ("Show surrounding parens", "<<flash-paren>>"),
- ])
- ]
- STYLE = idleConf.GetOption(
- 'extensions','ParenMatch','style', default='expression')
- FLASH_DELAY = idleConf.GetOption(
- 'extensions','ParenMatch','flash-delay', type='int',default=500)
- BELL = idleConf.GetOption(
- 'extensions','ParenMatch','bell', type='bool',default=1)
- HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(),'hilite')
RESTORE_VIRTUAL_EVENT_NAME = "<<parenmatch-check-restore>>"
# We want the restore event be called before the usual return and
self.is_restore_active = 0
self.set_style(self.STYLE)
+ @classmethod
+ def reload(cls):
+ cls.STYLE = idleConf.GetOption(
+ 'extensions','ParenMatch','style', default='opener')
+ cls.FLASH_DELAY = idleConf.GetOption(
+ 'extensions','ParenMatch','flash-delay', type='int',default=500)
+ cls.BELL = idleConf.GetOption(
+ 'extensions','ParenMatch','bell', type='bool', default=1)
+ cls.HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(),
+ 'hilite')
+
def activate_restore(self):
"Activate mechanism to restore text from highlighting."
if not self.is_restore_active:
lambda self=self, c=self.counter: self.handle_restore_timer(c))
+ParenMatch.reload()
+
+
if __name__ == '__main__':
import unittest
unittest.main('idlelib.idle_test.test_parenmatch', verbosity=2)
class RstripExtension:
- menudefs = [
- ('format', [None, ('Strip trailing whitespace', '<<do-rstrip>>'), ] ), ]
-
def __init__(self, editwin):
self.editwin = editwin
- self.editwin.text.bind("<<do-rstrip>>", self.do_rstrip)
def do_rstrip(self, event=None):
-"""Extension to execute code outside the Python shell window.
+"""Execute code from an editor.
-This adds the following commands:
+Check module: do a full syntax check of the current module.
+Also run the tabnanny to catch any inconsistent tabs.
-- Check module does a full syntax check of the current module.
- It also runs the tabnanny to catch any inconsistent tabs.
-
-- Run module executes the module's code in the __main__ namespace. The window
- must have been saved previously. The module is added to sys.modules, and is
- also added to the __main__ namespace.
-
-XXX GvR Redesign this interface (yet again) as follows:
-
-- Present a dialog box for ``Run Module''
-
-- Allow specify command line arguments in the dialog box
+Run module: also execute the module's code in the __main__ namespace.
+The window must have been saved previously. The module is added to
+sys.modules, and is also added to the __main__ namespace.
+TODO: Specify command line arguments in a dialog box.
"""
-
import os
import tabnanny
import tokenize
class ScriptBinding:
- menudefs = [
- ('run', [None,
- ('Check Module', '<<check-module>>'),
- ('Run Module', '<<run-module>>'), ]), ]
-
def __init__(self, editwin):
self.editwin = editwin
# Provide instance variables referenced by debugger
-# Sample extension: zoom a window to maximum height
+"Zoom a window to maximum height."
import re
import sys
class ZoomHeight:
- menudefs = [
- ('windows', [
- ('_Zoom Height', '<<zoom-height>>'),
- ])
- ]
-
def __init__(self, editwin):
self.editwin = editwin
--- /dev/null
+"Example extension, also used for testing."
+
+from idlelib.config import idleConf
+
+ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text')
+
+
+class ZzDummy:
+
+## menudefs = [
+## ('format', [
+## ('Z in', '<<z-in>>'),
+## ('Z out', '<<z-out>>'),
+## ] )
+## ]
+
+ def __init__(self, editwin):
+ self.text = editwin.text
+ z_in = False
+
+ @classmethod
+ def reload(cls):
+ cls.ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text')
+
+ def z_in_event(self, event):
+ """
+ """
+ text = self.text
+ text.undo_block_start()
+ for line in range(1, text.index('end')):
+ text.insert('%d.0', ztest)
+ text.undo_block_stop()
+ return "break"
+
+ def z_out_event(self, event): pass
+
+ZzDummy.reload()
+
+##if __name__ == "__main__":
+## import unittest
+## unittest.main('idlelib.idle_test.test_zzdummy',
+## verbosity=2, exit=False)
--- /dev/null
+Convert IDLE's built-in 'extensions' to regular features.
+
+About 10 IDLE features were implemented as supposedly optional
+extensions. Their different behavior could be confusing or worse for
+users and not good for maintenance. Hence the conversion.
+
+The main difference for users is that user configurable key bindings
+for builtin features are now handled uniformly. Now, editing a binding
+in a keyset only affects its value in the keyset. All bindings are
+defined together in the system-specific default keysets in config-
+extensions.def. All custom keysets are saved as a whole in config-
+extension.cfg. All take effect as soon as one clicks Apply or Ok.
+
+The affected events are '<<force-open-completions>>', '<<expand-word>>',
+'<<force-open-calltip>>', '<<flash-paren>>', '<<format-paragraph>>',
+'<<run-module>>', '<<check-module>>', and '<<zoom-height>>'. Any
+(global) customizations made before 3.6.3 will not affect their keyset-
+specific customization after 3.6.3. and vice versa.
+
+Inital patch by Charles Wohlganger.