-
Notifications
You must be signed in to change notification settings - Fork 4
Description
Inspired by this forum thread.
Some packages want to allow package settings to be overridden per view, per window, and/or per project. We recommend using a ChainMap of SettingsDicts for this. However, this only works if all of the Settings objects use the same keys. In practice, packages should and often do cordon off the package-specific parts of view, window, and project settings. This is usually done in one of two ways:
- Prefixing the key (e.g.
my_settingin the global package settings,Example Package-my_settingin the view settings). - Putting all package settings under a struct (e.g.
Example Package→my_settingin the project settings).
Even with SettingsDict and ChainMap, this requires boilerplate to implement. Consider the following code sample from ESLint-Formatter:
@staticmethod
def get_pref(key):
global_settings = sublime.load_settings(SETTINGS_FILE)
value = global_settings.get(key)
# Load active project settings
project_settings = sublime.active_window().active_view().settings()
# Overwrite global config value if it's defined
if project_settings.has(PROJECT_NAME):
value = project_settings.get(PROJECT_NAME).get(key, value)
return valueThis only handles two levels (package and project), is read-only, and relies on active_window(). but it's still a fair bit of code. Adding window and view settings would add yet more complication.
It would seem beneficial for sublime_lib to provide an abstraction for this pattern that is robust, full-featured, and easy to use. The simplest approach (in terms of implementation) would be to define simple dictionary decorators and rely on ChainMap:
settings = ChainMap(
PrefixedDict(SettingsDict(view.settings()), 'MySettings-'),
PrefixedDict(SettingsDict(window.settings()), 'MySettings-'),
NamedSettingsDict('MySettings')
)This is still a fair bit of boilerplate, though. In addition, the ChainMap is missing the subscribe functionality that SettingsDict provides; a plugin would have to subscribe to each individually and manually check whether the value has changed.
A more usable solution would be a single constructor:
settings = SettingsChain('MySettings', window)
settings = SettingsChain('MySettings', view) # includes window implicitly?This is a bit more opinionated than the user building their own stack piece-by-piece, but it's a lot more concise and would encourage consistency among packages that used it.
Questions:
- Does this generally sound like a reasonable feature to provide?
- What's the right balance of convenience without confusion?
- How should we handle modifying the chain? We could follow the ChainMap convention of modifying the top dict, or we could require that the user specify, e.g.
my_chain.view[key] = value. - What do we do about project data? It's not a real Settings, so even if we shimmed it there's no way to subscribe to changes.