-
-
Notifications
You must be signed in to change notification settings - Fork 33.2k
gh-89520: IDLE - Make extentions use user's keys, not all defaults #28713
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 19 commits
354ad86
b6d2782
48e18d6
d8a267c
e1e8cd9
e2a2528
8fe6ecd
539d63b
1dfacb9
4e2161b
b425b72
88d40e0
c835095
f0e856a
54b6c54
73c984e
40d864b
fc190e3
a2eb4b6
dc46fb7
59b6a25
016ce89
ba7218f
954d0b8
568cd80
0240959
c032cf8
2f47ecb
3a9d1dd
29961f3
8336d96
350253f
d6c8947
e521eef
9e04329
c0aaf33
e142132
455e8df
344d555
d814f03
7d0f5ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,7 @@ | |
| from configparser import ConfigParser | ||
| import os | ||
| import sys | ||
| from collections import ChainMap | ||
|
|
||
| from tkinter.font import Font | ||
| import idlelib | ||
|
|
@@ -192,7 +193,7 @@ def GetUserCfgDir(self): | |
| except OSError: | ||
| pass | ||
| userDir = '~' | ||
| if userDir == "~": # still no path to home! | ||
| if userDir == '~': # still no path to home! | ||
| # traditionally IDLE has defaulted to os.getcwd(), is this adequate? | ||
| userDir = os.getcwd() | ||
| userDir = os.path.join(userDir, cfgDir) | ||
|
|
@@ -208,7 +209,7 @@ def GetUserCfgDir(self): | |
| except OSError: | ||
| pass | ||
| raise SystemExit | ||
| # TODO continue without userDIr instead of exit | ||
| # TODO continue without userDir instead of exit | ||
| return userDir | ||
|
|
||
| def GetOption(self, configType, section, option, default=None, type=None, | ||
|
|
@@ -266,7 +267,7 @@ def GetSectionList(self, configSet, configType): | |
| if configSet == 'user': | ||
| cfgParser = self.userCfg[configType] | ||
| elif configSet == 'default': | ||
| cfgParser=self.defaultCfg[configType] | ||
| cfgParser = self.defaultCfg[configType] | ||
| else: | ||
| raise InvalidConfigSet('Invalid configSet specified') | ||
| return cfgParser.sections() | ||
|
|
@@ -402,7 +403,7 @@ def current_colors_and_keys(self, section): | |
|
|
||
| @staticmethod | ||
| def default_keys(): | ||
| if sys.platform[:3] == 'win': | ||
| if sys.platform.startswith('win'): | ||
| return 'IDLE Classic Windows' | ||
| elif sys.platform == 'darwin': | ||
| return 'IDLE Classic OSX' | ||
|
|
@@ -440,7 +441,7 @@ def GetExtensions(self, active_only=True, | |
| option = "enable_editor" | ||
| else: | ||
| option = "enable_shell" | ||
| if self.GetOption('extensions', extn,option, | ||
| if self.GetOption('extensions', extn, option, | ||
| default=True, type='bool', | ||
| warn_on_default=False): | ||
| activeExtns.append(extn) | ||
|
|
@@ -468,61 +469,95 @@ def GetExtnNameForEvent(self, virtualEvent): | |
| extName = extn # TODO return here? | ||
| return extName | ||
|
|
||
| def GetExtensionKeys(self, extensionName): | ||
| def GetExtensionKeys(self, extension_name): | ||
| """Return dict: {configurable extensionName event : active keybinding}. | ||
|
|
||
| Events come from default config extension_cfgBindings section. | ||
| Keybindings come from GetCurrentKeySet() active key dict, | ||
| where previously used bindings are disabled. | ||
| """ | ||
| keysName = extensionName + '_cfgBindings' | ||
| activeKeys = self.GetCurrentKeySet() | ||
| extKeys = {} | ||
| if self.defaultCfg['extensions'].has_section(keysName): | ||
| eventNames = self.defaultCfg['extensions'].GetOptionList(keysName) | ||
| for eventName in eventNames: | ||
| event = '<<' + eventName + '>>' | ||
| binding = activeKeys[event] | ||
| extKeys[event] = binding | ||
| return extKeys | ||
|
|
||
| def __GetRawExtensionKeys(self,extensionName): | ||
| bindings_section = f'{extension_name}_cfgBindings' | ||
| current_keyset = idleConf.GetCurrentKeySet() | ||
| extension_keys = {} | ||
|
|
||
| event_names = set() | ||
| if self.userCfg['extensions'].has_section(bindings_section): | ||
| event_names |= set( | ||
| self.userCfg['extensions'].GetOptionList(bindings_section) | ||
| ) | ||
| if self.defaultCfg['extensions'].has_section(bindings_section): | ||
| event_names |= set( | ||
| self.defaultCfg['extensions'].GetOptionList(bindings_section) | ||
| ) | ||
|
|
||
| for event_name in event_names: | ||
| event = f'<<{event_name}>>' | ||
| binding = current_keyset.get(event, None) | ||
| if binding is None: | ||
| continue | ||
| extension_keys[event] = binding | ||
| return extension_keys | ||
|
|
||
| def __GetRawExtensionKeys(self, extension_name): | ||
| """Return dict {configurable extensionName event : keybinding list}. | ||
|
|
||
| Events come from default config extension_cfgBindings section. | ||
| Keybindings list come from the splitting of GetOption, which | ||
| tries user config before default config. | ||
| """ | ||
| keysName = extensionName+'_cfgBindings' | ||
| extKeys = {} | ||
| if self.defaultCfg['extensions'].has_section(keysName): | ||
| eventNames = self.defaultCfg['extensions'].GetOptionList(keysName) | ||
| for eventName in eventNames: | ||
| binding = self.GetOption( | ||
| 'extensions', keysName, eventName, default='').split() | ||
| event = '<<' + eventName + '>>' | ||
| extKeys[event] = binding | ||
| return extKeys | ||
|
|
||
| def GetExtensionBindings(self, extensionName): | ||
| bindings_section = f'{extension_name}_cfgBindings' | ||
| extension_keys = {} | ||
|
|
||
| event_names = [] | ||
| if self.userCfg['extensions'].has_section(bindings_section): | ||
| event_names.append(self.userCfg['extensions'].GetOptionList(bindings_section)) | ||
| if self.defaultCfg['extensions'].has_section(bindings_section): | ||
| event_names.append(self.defaultCfg['extensions'].GetOptionList(bindings_section)) | ||
|
|
||
| # Because chain map, favors user bindings over default bindings | ||
| for event_name in ChainMap(*event_names): | ||
| binding = self.GetOption( | ||
| 'extensions', | ||
| bindings_section, | ||
| event_name, | ||
| default='', | ||
| ).split() | ||
| event = f'<<{event_name}>>' | ||
| extension_keys[event] = binding | ||
| return extension_keys | ||
|
|
||
| def GetExtensionBindings(self, extension_name): | ||
| """Return dict {extensionName event : active or defined keybinding}. | ||
|
|
||
| Augment self.GetExtensionKeys(extensionName) with mapping of non- | ||
| configurable events (from default config) to GetOption splits, | ||
| as in self.__GetRawExtensionKeys. | ||
| """ | ||
| bindsName = extensionName + '_bindings' | ||
| extBinds = self.GetExtensionKeys(extensionName) | ||
| #add the non-configurable bindings | ||
| if self.defaultCfg['extensions'].has_section(bindsName): | ||
| eventNames = self.defaultCfg['extensions'].GetOptionList(bindsName) | ||
| for eventName in eventNames: | ||
| binding = self.GetOption( | ||
| 'extensions', bindsName, eventName, default='').split() | ||
| event = '<<' + eventName + '>>' | ||
| extBinds[event] = binding | ||
|
|
||
| return extBinds | ||
| bindings_section = f'{extension_name}_bindings' | ||
| extension_keys = self.GetExtensionKeys(extension_name) | ||
|
|
||
| # add the non-configurable bindings | ||
| values = [] | ||
| if self.userCfg['extensions'].has_section(bindings_section): | ||
| values.append( | ||
| self.userCfg['extensions'].GetOptionList(bindings_section) | ||
| ) | ||
| if self.defaultCfg['extensions'].has_section(bindings_section): | ||
| values.append( | ||
| self.defaultCfg['extensions'].GetOptionList(bindings_section) | ||
| ) | ||
|
|
||
| # Because chain map, favors user bindings over default bindings | ||
| for event_name in ChainMap(*values): | ||
| binding = self.GetOption( | ||
| 'extensions', | ||
| bindings_section, | ||
| event_name, | ||
| default='' | ||
| ).split() | ||
| event = f'<<{event_name}>>' | ||
| extension_keys[event] = binding | ||
| return extension_keys | ||
|
|
||
| def GetKeyBinding(self, keySetName, eventStr): | ||
| """Return the keybinding list for keySetName eventStr. | ||
|
|
@@ -703,7 +738,7 @@ def GetExtraHelpSourceList(self, configSet): | |
| cfgParser = self.defaultCfg['main'] | ||
| else: | ||
| raise InvalidConfigSet('Invalid configSet specified') | ||
| options=cfgParser.GetOptionList('HelpFiles') | ||
| options = cfgParser.GetOptionList('HelpFiles') | ||
| for option in options: | ||
| value=cfgParser.Get('HelpFiles', option, default=';') | ||
| if value.find(';') == -1: #malformed config entry with no ';' | ||
|
|
@@ -714,7 +749,7 @@ def GetExtraHelpSourceList(self, configSet): | |
| menuItem=value[0].strip() | ||
| helpPath=value[1].strip() | ||
| if menuItem and helpPath: #neither are empty strings | ||
| helpSources.append( (menuItem,helpPath,option) ) | ||
| helpSources.append( (menuItem, helpPath, option) ) | ||
| helpSources.sort(key=lambda x: x[2]) | ||
| return helpSources | ||
|
|
||
|
|
@@ -757,7 +792,8 @@ def LoadCfgFiles(self): | |
| "Load all configuration files." | ||
| for key in self.defaultCfg: | ||
| self.defaultCfg[key].Load() | ||
| self.userCfg[key].Load() #same keys | ||
| for key in self.userCfg: | ||
| self.userCfg[key].Load() | ||
|
Comment on lines
+796
to
+797
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change looks wrong, as it would load any garbage that users add to the user file. Since it affects all confit files, not obvious related to this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change allows keys that are not in the default configuration to be loaded from user configuration. This means sections like extensions that might not be in the default config can be loaded. |
||
|
|
||
| def SaveUserCfgFiles(self): | ||
| "Write all loaded user configuration files to disk." | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Make idlelib.config.idleConf's functions GetExtensionKeys, __GetRawExtensionKeys, and GetKeyBinding look at user config files, allowing custom extensions to have key-binds defined in ~/.idlerc instead of in the default key-binds file. |
Uh oh!
There was an error while loading. Please reload this page.