Kaydet (Commit) 9bdb1edf authored tarafından Terry Jan Reedy's avatar Terry Jan Reedy

Issue #27173: Add 'IDLE Modern Unix' to the built-in key sets.

Make the default key set depend on the platform.
Add tests for changes to the config module.
üst ce85acff
...@@ -109,6 +109,57 @@ change-indentwidth=<Alt-Key-u> ...@@ -109,6 +109,57 @@ change-indentwidth=<Alt-Key-u>
del-word-left=<Alt-Key-BackSpace> del-word-left=<Alt-Key-BackSpace>
del-word-right=<Alt-Key-d> del-word-right=<Alt-Key-d>
[IDLE Modern Unix]
copy = <Control-Shift-Key-C> <Control-Key-Insert>
cut = <Control-Key-x> <Shift-Key-Delete>
paste = <Control-Key-v> <Shift-Key-Insert>
beginning-of-line = <Key-Home>
center-insert = <Control-Key-l>
close-all-windows = <Control-Key-q>
close-window = <Control-Key-w> <Control-Shift-Key-W>
do-nothing = <Control-Key-F12>
end-of-file = <Control-Key-d>
history-next = <Alt-Key-n> <Meta-Key-n>
history-previous = <Alt-Key-p> <Meta-Key-p>
interrupt-execution = <Control-Key-c>
view-restart = <Key-F6>
restart-shell = <Control-Key-F6>
open-class-browser = <Control-Key-b>
open-module = <Control-Key-m>
open-new-window = <Control-Key-n>
open-window-from-file = <Control-Key-o>
plain-newline-and-indent = <Control-Key-j>
print-window = <Control-Key-p>
python-context-help = <Shift-Key-F1>
python-docs = <Key-F1>
redo = <Control-Shift-Key-Z>
remove-selection = <Key-Escape>
save-copy-of-window-as-file = <Alt-Shift-Key-S>
save-window-as-file = <Control-Shift-Key-S>
save-window = <Control-Key-s>
select-all = <Control-Key-a>
toggle-auto-coloring = <Control-Key-slash>
undo = <Control-Key-z>
find = <Control-Key-f>
find-again = <Key-F3>
find-in-files = <Control-Shift-Key-f>
find-selection = <Control-Key-h>
replace = <Control-Key-r>
goto-line = <Control-Key-g>
smart-backspace = <Key-BackSpace>
newline-and-indent = <Key-Return> <Key-KP_Enter>
smart-indent = <Key-Tab>
indent-region = <Control-Key-bracketright>
dedent-region = <Control-Key-bracketleft>
comment-region = <Control-Key-d>
uncomment-region = <Control-Shift-Key-D>
tabify-region = <Alt-Key-5>
untabify-region = <Alt-Key-6>
toggle-tabs = <Control-Key-T>
change-indentwidth = <Alt-Key-u>
del-word-left = <Control-Key-BackSpace>
del-word-right = <Control-Key-Delete>
[IDLE Classic Mac] [IDLE Classic Mac]
copy=<Command-Key-c> copy=<Command-Key-c>
cut=<Command-Key-x> cut=<Command-Key-x>
......
...@@ -70,7 +70,9 @@ name2= ...@@ -70,7 +70,9 @@ name2=
[Keys] [Keys]
default= 1 default= 1
name= IDLE Classic Windows name=
name2=
# name2 set in user config-main.cfg for keys added after 2016 July 1
[History] [History]
cyclic=1 cyclic=1
......
...@@ -234,10 +234,7 @@ class IdleConf: ...@@ -234,10 +234,7 @@ class IdleConf:
' from section %r: %r' % ' from section %r: %r' %
(type, option, section, (type, option, section,
self.userCfg[configType].Get(section, option, raw=raw))) self.userCfg[configType].Get(section, option, raw=raw)))
try: _warn(warning, configType, section, option)
print(warning, file=sys.stderr)
except OSError:
pass
try: try:
if self.defaultCfg[configType].has_option(section,option): if self.defaultCfg[configType].has_option(section,option):
return self.defaultCfg[configType].Get( return self.defaultCfg[configType].Get(
...@@ -251,10 +248,7 @@ class IdleConf: ...@@ -251,10 +248,7 @@ class IdleConf:
' from section %r.\n' ' from section %r.\n'
' returning default value: %r' % ' returning default value: %r' %
(option, section, default)) (option, section, default))
try: _warn(warning, configType, section, option)
print(warning, file=sys.stderr)
except OSError:
pass
return default return default
def SetOption(self, configType, section, option, value): def SetOption(self, configType, section, option, value):
...@@ -362,47 +356,68 @@ class IdleConf: ...@@ -362,47 +356,68 @@ class IdleConf:
'\n from theme %r.\n' '\n from theme %r.\n'
' returning default color: %r' % ' returning default color: %r' %
(element, themeName, theme[element])) (element, themeName, theme[element]))
try: _warn(warning, 'highlight', themeName, element)
print(warning, file=sys.stderr)
except OSError:
pass
theme[element] = cfgParser.Get( theme[element] = cfgParser.Get(
themeName, element, default=theme[element]) themeName, element, default=theme[element])
return theme return theme
def CurrentTheme(self): def CurrentTheme(self):
"""Return the name of the currently active text color theme. "Return the name of the currently active text color theme."
return self.current_colors_and_keys('Theme')
def CurrentKeys(self):
"""Return the name of the currently active key set."""
return self.current_colors_and_keys('Keys')
def current_colors_and_keys(self, section):
"""Return the currently active name for Theme or Keys section.
idlelib.config-main.def ('default') includes these sections
idlelib.config-main.def includes this section
[Theme] [Theme]
default= 1 default= 1
name= IDLE Classic name= IDLE Classic
name2= name2=
# name2 set in user config-main.cfg for themes added after 2015 Oct 1
Item name2 is needed because setting name to a new builtin [Keys]
causes older IDLEs to display multiple error messages or quit. default= 1
name=
name2=
Item 'name2', is used for built-in ('default') themes and keys
added after 2015 Oct 1 and 2016 July 1. This kludge is needed
because setting 'name' to a builtin not defined in older IDLEs
to display multiple error messages or quit.
See https://bugs.python.org/issue25313. See https://bugs.python.org/issue25313.
When default = True, name2 takes precedence over name, When default = True, 'name2' takes precedence over 'name',
while older IDLEs will just use name. while older IDLEs will just use name. When default = False,
'name2' may still be set, but it is ignored.
""" """
cfgname = 'highlight' if section == 'Theme' else 'keys'
default = self.GetOption('main', 'Theme', 'default', default = self.GetOption('main', 'Theme', 'default',
type='bool', default=True) type='bool', default=True)
name = ''
if default: if default:
theme = self.GetOption('main', 'Theme', 'name2', default='') name = self.GetOption('main', section, 'name2', default='')
if default and not theme or not default: if not name:
theme = self.GetOption('main', 'Theme', 'name', default='') name = self.GetOption('main', section, 'name', default='')
source = self.defaultCfg if default else self.userCfg if name:
if source['highlight'].has_section(theme): source = self.defaultCfg if default else self.userCfg
return theme if source[cfgname].has_section(name):
return name
return "IDLE Classic" if section == 'Theme' else self.default_keys()
@staticmethod
def default_keys():
if sys.platform[:3] == 'win':
return 'IDLE Classic Windows'
elif sys.platform == 'darwin':
return 'IDLE Classic OSX'
else: else:
return "IDLE Classic" return 'IDLE Modern Unix'
def CurrentKeys(self): def GetExtensions(self, active_only=True,
"Return the name of the currently active key set." editor_only=False, shell_only=False):
return self.GetOption('main', 'Keys', 'name', default='')
def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
"""Return extensions in default and user config-extensions files. """Return extensions in default and user config-extensions files.
If active_only True, only return active (enabled) extensions If active_only True, only return active (enabled) extensions
...@@ -422,7 +437,7 @@ class IdleConf: ...@@ -422,7 +437,7 @@ class IdleConf:
if self.GetOption('extensions', extn, 'enable', default=True, if self.GetOption('extensions', extn, 'enable', default=True,
type='bool'): type='bool'):
#the extension is enabled #the extension is enabled
if editor_only or shell_only: # TODO if both, contradictory if editor_only or shell_only: # TODO both True contradict
if editor_only: if editor_only:
option = "enable_editor" option = "enable_editor"
else: else:
...@@ -527,7 +542,8 @@ class IdleConf: ...@@ -527,7 +542,8 @@ class IdleConf:
eventStr - virtual event, including brackets, as in '<<event>>'. eventStr - virtual event, including brackets, as in '<<event>>'.
""" """
eventName = eventStr[2:-2] #trim off the angle brackets eventName = eventStr[2:-2] #trim off the angle brackets
binding = self.GetOption('keys', keySetName, eventName, default='').split() binding = self.GetOption('keys', keySetName, eventName, default='',
warn_on_default=False).split()
return binding return binding
def GetCurrentKeySet(self): def GetCurrentKeySet(self):
...@@ -638,20 +654,28 @@ class IdleConf: ...@@ -638,20 +654,28 @@ class IdleConf:
'<<del-word-right>>': ['<Control-Key-Delete>'] '<<del-word-right>>': ['<Control-Key-Delete>']
} }
if keySetName: if keySetName:
for event in keyBindings: if not (self.userCfg['keys'].has_section(keySetName) or
binding = self.GetKeyBinding(keySetName, event) self.defaultCfg['keys'].has_section(keySetName)):
if binding: warning = (
keyBindings[event] = binding '\n Warning: config.py - IdleConf.GetCoreKeys -\n'
else: #we are going to return a default, print warning ' key set %r is not defined, using default bindings.' %
warning=('\n Warning: config.py - IdleConf.GetCoreKeys' (keySetName,)
' -\n problem retrieving key binding for event %r' )
'\n from key set %r.\n' _warn(warning, 'keys', keySetName)
' returning default value: %r' % else:
(event, keySetName, keyBindings[event])) for event in keyBindings:
try: binding = self.GetKeyBinding(keySetName, event)
print(warning, file=sys.stderr) if binding:
except OSError: keyBindings[event] = binding
pass else: #we are going to return a default, print warning
warning = (
'\n Warning: config.py - IdleConf.GetCoreKeys -\n'
' problem retrieving key binding for event %r\n'
' from key set %r.\n'
' returning default value: %r' %
(event, keySetName, keyBindings[event])
)
_warn(warning, 'keys', keySetName, event)
return keyBindings return keyBindings
def GetExtraHelpSourceList(self, configSet): def GetExtraHelpSourceList(self, configSet):
...@@ -735,6 +759,18 @@ class IdleConf: ...@@ -735,6 +759,18 @@ class IdleConf:
idleConf = IdleConf() idleConf = IdleConf()
_warned = set()
def _warn(msg, *key):
key = (msg,) + key
if key not in _warned:
try:
print(msg, file=sys.stderr)
except OSError:
pass
_warned.add(key)
# TODO Revise test output, write expanded unittest # TODO Revise test output, write expanded unittest
# #
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -341,6 +341,7 @@ class ConfigDialog(Toplevel): ...@@ -341,6 +341,7 @@ class ConfigDialog(Toplevel):
buttonSaveCustomKeys = Button( buttonSaveCustomKeys = Button(
frames[1], text='Save as New Custom Key Set', frames[1], text='Save as New Custom Key Set',
command=self.SaveAsNewKeySet) command=self.SaveAsNewKeySet)
self.new_custom_keys = Label(frames[0], bd=2)
##widget packing ##widget packing
#body #body
...@@ -361,6 +362,7 @@ class ConfigDialog(Toplevel): ...@@ -361,6 +362,7 @@ class ConfigDialog(Toplevel):
self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS) self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS)
self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW) self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW)
self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW) self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW)
self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
frames[0].pack(side=TOP, fill=BOTH, expand=True) frames[0].pack(side=TOP, fill=BOTH, expand=True)
...@@ -514,10 +516,11 @@ class ConfigDialog(Toplevel): ...@@ -514,10 +516,11 @@ class ConfigDialog(Toplevel):
self.OnNewColourSet() self.OnNewColourSet()
def VarChanged_builtinTheme(self, *params): def VarChanged_builtinTheme(self, *params):
oldthemes = ('IDLE Classic', 'IDLE New')
value = self.builtinTheme.get() value = self.builtinTheme.get()
if value == 'IDLE Dark': if value not in oldthemes:
if idleConf.GetOption('main', 'Theme', 'name') != 'IDLE New': if idleConf.GetOption('main', 'Theme', 'name') not in oldthemes:
self.AddChangedItem('main', 'Theme', 'name', 'IDLE Classic') self.AddChangedItem('main', 'Theme', 'name', oldthemes[0])
self.AddChangedItem('main', 'Theme', 'name2', value) self.AddChangedItem('main', 'Theme', 'name2', value)
self.new_custom_theme.config(text='New theme, see Help', self.new_custom_theme.config(text='New theme, see Help',
fg='#500000') fg='#500000')
...@@ -557,8 +560,23 @@ class ConfigDialog(Toplevel): ...@@ -557,8 +560,23 @@ class ConfigDialog(Toplevel):
self.AddChangedItem('extensions', extKeybindSection, event, value) self.AddChangedItem('extensions', extKeybindSection, event, value)
def VarChanged_builtinKeys(self, *params): def VarChanged_builtinKeys(self, *params):
oldkeys = (
'IDLE Classic Windows',
'IDLE Classic Unix',
'IDLE Classic Mac',
'IDLE Classic OSX',
)
value = self.builtinKeys.get() value = self.builtinKeys.get()
self.AddChangedItem('main', 'Keys', 'name', value) if value not in oldkeys:
if idleConf.GetOption('main', 'Keys', 'name') not in oldkeys:
self.AddChangedItem('main', 'Keys', 'name', oldkeys[0])
self.AddChangedItem('main', 'Keys', 'name2', value)
self.new_custom_keys.config(text='New key set, see Help',
fg='#500000')
else:
self.AddChangedItem('main', 'Keys', 'name', value)
self.AddChangedItem('main', 'Keys', 'name2', '')
self.new_custom_keys.config(text='', fg='black')
self.LoadKeysList(value) self.LoadKeysList(value)
def VarChanged_customKeys(self, *params): def VarChanged_customKeys(self, *params):
...@@ -767,8 +785,10 @@ class ConfigDialog(Toplevel): ...@@ -767,8 +785,10 @@ class ConfigDialog(Toplevel):
else: else:
self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
#revert to default key set #revert to default key set
self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default')) self.keysAreBuiltin.set(idleConf.defaultCfg['main']
self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')) .Get('Keys', 'default'))
self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
or idleConf.default_keys())
#user can't back out of these changes, they must be applied now #user can't back out of these changes, they must be applied now
self.SaveAllChangedConfigs() self.SaveAllChangedConfigs()
self.ActivateConfigChanges() self.ActivateConfigChanges()
...@@ -1067,7 +1087,7 @@ class ConfigDialog(Toplevel): ...@@ -1067,7 +1087,7 @@ class ConfigDialog(Toplevel):
self.optMenuKeysCustom.SetMenu(itemList, currentOption) self.optMenuKeysCustom.SetMenu(itemList, currentOption)
itemList = idleConf.GetSectionList('default', 'keys') itemList = idleConf.GetSectionList('default', 'keys')
itemList.sort() itemList.sort()
self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0]) self.optMenuKeysBuiltin.SetMenu(itemList, idleConf.default_keys())
self.SetKeysType() self.SetKeysType()
##load keyset element list ##load keyset element list
keySetName = idleConf.CurrentKeys() keySetName = idleConf.CurrentKeys()
...@@ -1369,12 +1389,18 @@ machine. Some do not take affect until IDLE is restarted. ...@@ -1369,12 +1389,18 @@ machine. Some do not take affect until IDLE is restarted.
[Cancel] only cancels changes made since the last save. [Cancel] only cancels changes made since the last save.
''' '''
help_pages = { help_pages = {
'Highlighting':''' 'Highlighting': '''
Highlighting: Highlighting:
The IDLE Dark color theme is new in October 2015. It can only The IDLE Dark color theme is new in October 2015. It can only
be used with older IDLE releases if it is saved as a custom be used with older IDLE releases if it is saved as a custom
theme, with a different name. theme, with a different name.
''' ''',
'Keys': '''
Keys:
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.
''',
} }
......
'''Test idlelib.config.
Much is tested by opening config dialog live or in test_configdialog.
Coverage: 27%
'''
from sys import modules
from test.support import captured_stderr
from tkinter import Tk
import unittest
from idlelib import config
# Tests should not depend on fortuitous user configurations.
# They must not affect actual user .cfg files.
# Replace user parsers with empty parsers that cannot be saved.
idleConf = config.idleConf
usercfg = idleConf.userCfg
testcfg = {}
usermain = testcfg['main'] = config.IdleUserConfParser('') # filename
userhigh = testcfg['highlight'] = config.IdleUserConfParser('')
userkeys = testcfg['keys'] = config.IdleUserConfParser('')
def setUpModule():
idleConf.userCfg = testcfg
def tearDownModule():
idleConf.userCfg = testcfg
class CurrentColorKeysTest(unittest.TestCase):
"""Test correct scenarios for colorkeys and wrap functions.
The 5 correct patterns are possible results of config dialog.
"""
colorkeys = idleConf.current_colors_and_keys
def test_old_default(self):
# name2 must be blank
usermain.read_string('''
[Theme]
default= 1
''')
self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic')
usermain['Theme']['name'] = 'IDLE New'
self.assertEqual(self.colorkeys('Theme'), 'IDLE New')
usermain['Theme']['name'] = 'non-default' # error
self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic')
usermain.remove_section('Theme')
def test_new_default(self):
# name2 overrides name
usermain.read_string('''
[Theme]
default= 1
name= IDLE New
name2= IDLE Dark
''')
self.assertEqual(self.colorkeys('Theme'), 'IDLE Dark')
usermain['Theme']['name2'] = 'non-default' # error
self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic')
usermain.remove_section('Theme')
def test_user_override(self):
# name2 does not matter
usermain.read_string('''
[Theme]
default= 0
name= Custom Dark
''') # error until set userhigh
self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic')
userhigh.read_string('[Custom Dark]\na=b')
self.assertEqual(self.colorkeys('Theme'), 'Custom Dark')
usermain['Theme']['name2'] = 'IDLE Dark'
self.assertEqual(self.colorkeys('Theme'), 'Custom Dark')
usermain.remove_section('Theme')
userhigh.remove_section('Custom Dark')
class WarningTest(unittest.TestCase):
def test_warn(self):
Equal = self.assertEqual
config._warned = set()
with captured_stderr() as stderr:
config._warn('warning', 'key')
Equal(config._warned, {('warning','key')})
Equal(stderr.getvalue(), 'warning'+'\n')
with captured_stderr() as stderr:
config._warn('warning', 'key')
Equal(stderr.getvalue(), '')
with captured_stderr() as stderr:
config._warn('warn2', 'yek')
Equal(config._warned, {('warning','key'), ('warn2','yek')})
Equal(stderr.getvalue(), 'warn2'+'\n')
if __name__ == '__main__':
unittest.main(verbosity=2)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment