diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index 7a3fbe816..0facf9eb7 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -27,6 +27,7 @@ from configparser import (ConfigParser, ExtendedInterpolation, NoSectionError, from qutebrowser.utils.misc import read_file import qutebrowser.config.options as opt +import qutebrowser.config.sections as sect config = None state = None @@ -54,7 +55,7 @@ class ConfigStructure: def __init__(self): self.config = OrderedDict([ - ('general', KeyValueSection( + ('general', sect.KeyValue( ('show_completion', opt.ShowCompletion()), ('completion_height', opt.CompletionHeight()), ('ignorecase', opt.IgnoreCase()), @@ -64,7 +65,7 @@ class ConfigStructure: ('zoomlevels', opt.ZoomLevels()), ('defaultzoom', opt.DefaultZoom()), )), - ('tabbar', KeyValueSection( + ('tabbar', sect.KeyValue( ('movable', opt.Movable()), ('closebuttons', opt.CloseButtons()), ('scrollbuttons', opt.ScrollButtons()), @@ -72,16 +73,10 @@ class ConfigStructure: ('select_on_remove', opt.SelectOnRemove()), ('last_close', opt.LastClose()), )), - ('searchengines', ValueListSection( - opt.SearchEngineKeyValue() - )), - ('keybind', ValueListSection( - opt.KeybindKeyValue() - )), - ('aliases', ValueListSection( - opt.AliasKeyValue() - )), - ('colors', KeyValueSection( + ('searchengines', sect.SearchEngines()), + ('keybind', sect.KeyBindings()), + ('aliases', sect.Aliases()), + ('colors', sect.KeyValue( ('completion.fg', opt.CompletionFgColor()), ('completion.item.bg', opt.CompletionItemBgColor()), ('completion.category.bg', opt.CompletionCategoryBgColor()), diff --git a/qutebrowser/config/options.py b/qutebrowser/config/options.py index bb6902621..e9f51121c 100644 --- a/qutebrowser/config/options.py +++ b/qutebrowser/config/options.py @@ -33,17 +33,17 @@ class CompletionHeight(template.SettingValue): default = "50%" - def validate(self, value): - if value.endswith('%'): + def validate(self): + if self.value.endswith('%'): try: - intval = int(value.rstrip('%')) + intval = int(self.value.rstrip('%')) except ValueError: return False else: return 0 <= intval <= 100 else: try: - intval = int(value) + intval = int(self.value) except ValueError: return False else: @@ -75,21 +75,21 @@ class AutoSearch(template.BoolSettingValue): """Whether to start a search when something else than an URL is entered.""" - values = [("naive", "Use simple/naive check."), - ("dns", "Use DNS requests (might be slow!)."), - ("false", "Never search automatically.")] + valid_values = [("naive", "Use simple/naive check."), + ("dns", "Use DNS requests (might be slow!)."), + ("false", "Never search automatically.")] default = "naive" - def validate(self, value): - if value.lower() in ["naive", "dns"]: + def validate(self): + if self.value.lower() in ["naive", "dns"]: return True else: - return super().validate(value) + return super().validate(self.value) - def transform(self, value): - if value.lower() in ["naive", "dns"]: - return value.lower() - elif super().transform(value): + def transform(self): + if self.value.lower() in ["naive", "dns"]: + return self.value.lower() + elif super().transform(self.value): # boolean true is an alias for naive matching return "naive" else: @@ -137,7 +137,7 @@ class Position(template.SettingValue): """The position of the tab bar.""" - values = ["north", "south", "east", "west"] + valid_values = ["north", "south", "east", "west"] default = "north" @@ -145,9 +145,9 @@ class SelectOnRemove(template.SettingValue): """Which tab to select when the focused tab is removed.""" - values = [("left", "Select the tab on the left."), - ("right", "Select the tab on the right."), - ("previous", "Select the previously selected tab.")] + valid_values = [("left", "Select the tab on the left."), + ("right", "Select the tab on the right."), + ("previous", "Select the previously selected tab.")] default = "previous" @@ -155,9 +155,9 @@ class LastClose(template.SettingValue): """Behaviour when the last tab is closed.""" - values = [("ignore", "Don't do anything."), - ("blank", "Load about:blank."), - ("quit", "Quit qutebrowser.")] + valid_values = [("ignore", "Don't do anything."), + ("blank", "Load about:blank."), + ("quit", "Quit qutebrowser.")] default = "ignore" ### FIXME what to do with list-style sections? @@ -167,8 +167,8 @@ class SearchEngine(template.SettingValue): """A search engine setting.""" - def validate(self, value): - return "{}" in value + def validate(self): + return "{}" in self.value class CompletionFgColor(template.ColorSettingValue): @@ -260,7 +260,8 @@ class StatusbarFgErrorColor(template.ColorSettingValue): """Foreground color of the statusbar if there was an error.""" - default = "${statusbar.fg}" + default = StatusbarFgColor.default + default_conf = "${statusbar.fg}" class StatusbarBgErrorColor(template.ColorSettingValue): @@ -281,7 +282,8 @@ class StatusbarUrlFgColor(template.ColorSettingValue): """Default foreground color of the URL in the statusbar.""" - default = "${statusbar.fg}" + default = StatusbarFgColor.default + default_conf = "${statusbar.fg}" class StatusbarUrlSuccessFgColor(template.ColorSettingValue): @@ -353,18 +355,21 @@ class CompletionFont(template.FontSettingValue): """Font used in the completion widget.""" - default = "8pt ${_monospace}" + default = MonospaceFonts.default + default_conf = "8pt ${_monospace}" class TabbarFont(template.FontSettingValue): """Font used in the tabbar.""" - default = "8pt ${_monospace}" + default = MonospaceFonts.default + default_conf = "8pt ${_monospace}" class StatusbarFont(template.FontSettingValue): """Font used in the statusbar.""" - default = "8pt ${_monospace}" + default = MonospaceFonts.default + default_conf = "8pt ${_monospace}" diff --git a/qutebrowser/config/templates.py b/qutebrowser/config/templates.py index 791f9fd3d..8de73e135 100644 --- a/qutebrowser/config/templates.py +++ b/qutebrowser/config/templates.py @@ -22,50 +22,84 @@ import qutebrowser.commands.utils as cmdutils class SettingValue: - """Base class for settings. The docstring is used as a description.""" + """Base class for setting values. - # Possible values, if they are fixed. - # Either a list of strings, or a list of (value, desc) tuples. - values = None + Intended to be subclassed by config value "types". - # Default value if user has not overriden it, as a string. + Attributes: + valid_values: Possible values if they can be expressed as a fixed + string. Either a list of strings, or a list of (value, + desc) tuples. + # FIXME actually handle tuples and stuff when validating + + default: Default value if the user has not overridden it, as a string. + default_conf: Default value for the config, with interpolation. + value: (property) The currently valid, most important, transformed + value. + rawvalue: The current value as a raw string. + typestr: The name of the type to appear in the config. + + """ + + valid_values = None default = None + default_conf = None + typestr = None + rawvalue = None - def transform(self, value): + def __init__(self, rawval=None): + """Constructor. + + Args: + rawval: Raw value to set. + + """ + if rawval is not None: + self.rawvalue = rawval + + def __str__(self): + """Get raw string value.""" + return self.rawvalue if self.rawvalue is not None else '' + + @property + def value(self): + """Get the currently valid value.""" + # FIXME handle default properly + #if self._rawvalue is not None: + # val = self.rawvalue + #else: + # val = self.default + return self.transform() + + def transform(self): """Transform the setting value. This method can assume the value is indeed a valid value. The default implementation returns the original value. - Args: - value: The value to transform. - Return: The transformed value. """ - return value + return self.value - def validate(self, value): + def validate(self): """Validate value against possible values. - The default implementation checks the value against self.values if it - was defined. - - Args: - value: The value to validate. + The default implementation checks the value against self.valid_values + if it was defined. Return: Ture if validation succeeded, False otherwise. Raise: - NotImplementedError if self.values is not defined and this method - should be overridden. + NotImplementedError if self.valid_values is not defined and this + method should be overridden. """ - if self.values is not None: - return value in self.values + if self.valid_values is not None: + return self.value in self.valid_values else: raise NotImplementedError @@ -74,29 +108,32 @@ class BoolSettingValue(SettingValue): """Base class for a boolean setting.""" - values = ['true', 'false'] + valid_values = ['true', 'false'] + typestr = 'bool' # Taken from configparser _BOOLEAN_STATES = {'1': True, 'yes': True, 'true': True, 'on': True, '0': False, 'no': False, 'false': False, 'off': False} - def transform(self, value): - return self._BOOLEAN_STATES[value.lower()] + def transform(self): + return self._BOOLEAN_STATES[self.value.lower()] - def validate(self, value): - return value.lower() in self._BOOLEAN_STATES + def validate(self): + return self.value.lower() in self._BOOLEAN_STATES class IntSettingValue(SettingValue): """Base class for an integer setting.""" - def transform(self, value): - return int(value) + typestr = 'int' - def validate(self, value): + def transform(self): + return int(self.value) + + def validate(self): try: - int(value) + int(self.value) except ValueError: return False else: @@ -107,10 +144,12 @@ class ListSettingValue(SettingValue): """Base class for a (string-)list setting.""" - def transform(self, value): - return value.split(',') + typestr = 'string-list' - def validate(self, value): + def transform(self): + return self.value.split(',') + + def validate(self): return True @@ -118,13 +157,15 @@ class IntListSettingValue(ListSettingValue): """Base class for an int-list setting.""" - def transform(self, value): - vals = super().transform(value) + typestr = 'int-list' + + def transform(self): + vals = super().transform(self.value) return map(int, vals) - def validate(self, value): + def validate(self): try: - self.transform(value) + self.transform(self.value) except ValueError: return False else: @@ -135,15 +176,17 @@ class CommandSettingValue(SettingValue): """Base class for a command value with arguments.""" - values = cmdutils.cmd_dict.values() + typestr = 'command' - def validate(self, value): + valid_values = cmdutils.cmd_dict.items() + + def validate(self): # We need to import this here to avoid circular dependencies from qutebrowser.commands.parsers import (CommandParser, NoSuchCommandError) cp = CommandParser() try: - cp.parse(value) + cp.parse(self.value) except NoSuchCommandError: return False else: @@ -154,7 +197,9 @@ class ColorSettingValue(SettingValue): """Base class for a color value.""" - def validate(self, value): + typestr = 'color' + + def validate(self): # FIXME validate colors return True @@ -163,6 +208,35 @@ class FontSettingValue(SettingValue): """Base class for a font value.""" - def validate(self, value): + typestr = 'font' + + def validate(self): # FIXME validate fonts return True + + +class ValueListSection: + + """This class represents a section with a list key-value settings. + + These are settings inside sections which don't have fixed keys, but instead + have a dynamic list of "key = value" pairs, like keybindings or + searchengines. + + They basically consist of two different SettingValues and have no defaults. + + Attributes: + values: An OrderedDict with key as index and value as value. + default: An OrderedDict with the default configuration as strings. + types: A tuple for (keytype, valuetype) + + """ + + values = None + default = None + types = None + + def __str__(self): + """Get the key = value pairs as a string.""" + return '\n'.join('{} = {}'.format(key.rawvalue, val.rawvalue) + for key, val in self.values)