[3.6] bpo-27099: IDLE - Convert built-in extensions to regular features (GH-2494) (#3487)

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, revised by Terry Jan Reedy.
(cherry picked from commit 58fc71c)
This commit is contained in:
Terry Jan Reedy 2017-09-10 20:30:46 -04:00 committed by GitHub
parent 28580316a5
commit d6c397bf77
21 changed files with 451 additions and 272 deletions

View file

@ -15,7 +15,7 @@ from tkinter import (Toplevel, Listbox, Text, Scale, Canvas,
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
@ -198,7 +198,6 @@ class ConfigDialog(Toplevel):
def deactivate_current_config(self):
"""Remove current key bindings.
Iterate over window instances defined in parent and remove
the keybindings.
"""
@ -288,6 +287,7 @@ class ConfigDialog(Toplevel):
"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:
@ -796,7 +796,8 @@ class HighPage(Frame):
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'),
@ -858,11 +859,10 @@ class HighPage(Frame):
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)
@ -1764,15 +1764,30 @@ class GenPage(Frame):
(*)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')
@ -1790,7 +1805,7 @@ class GenPage(Frame):
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')
@ -1800,6 +1815,26 @@ class GenPage(Frame):
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) ')
@ -1810,6 +1845,18 @@ class GenPage(Frame):
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)
@ -1847,11 +1894,33 @@ class GenPage(Frame):
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)
@ -1863,17 +1932,30 @@ class GenPage(Frame):
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')
@ -2034,10 +2116,10 @@ The IDLE Modern Unix key set is new in June 2016. It can only
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.