from textual.app import App from pathlib import Path import os import configparser from typing import Any class ConfigHandler: def __init__(self, app: App): self.app: App = app self.config: configparser.ConfigParser = configparser.ConfigParser() self.plugin_defined_settings = {} self.config_dir: str = self.ensure_hidden_config_dir() self.load_settings() def define_plugin_setting(self, plugin_name: str, option_name: str, description: str, option_type: str, default_value: Any, on_changed_func = None): actual_type = None if option_type == "boolean": actual_type = bool elif option_type == "string": actual_type = str elif option_type == "float": actual_type = float elif option_type == "integer": actual_type = int else: raise Exception(f"Invalid type name \"{option_type}\".") if plugin_name in self.plugin_defined_settings: self.plugin_defined_settings[plugin_name][option_name] = { "option_name": option_name, "description": description, "type": actual_type, "default_value": default_value, "on_changed_func": on_changed_func } else: self.plugin_defined_settings[plugin_name] = {option_name: { "option_name": option_name, "description": description, "type": actual_type, "default_value": default_value, "on_changed_func": on_changed_func }} # user hasnt used this plugin before if self.get("plugin_" + plugin_name, option_name) == None: self.set("plugin_" + plugin_name, option_name, default_value) def ensure_hidden_config_dir(self): config_dir = Path.home() / ".berry" config_dir.mkdir(parents=True, exist_ok=True) return config_dir def get(self, section: str, option: str): if not self.config.has_section(section): self.config.add_section(section) if not self.config.has_option(section, option): self.set(section, option, "0") return "0" return self.config.get(section, option, fallback=None) def set(self, section: str, option: str, new_value: str): if not self.config.has_section(section): self.config.add_section(section) self.config.set(section, option, new_value) if section.startswith("plugin_"): section = section.removeprefix("plugin_") if self.plugin_defined_settings[section].get("on_changed_func", None) != None: self.plugin_defined_settings[section]["on_changed_func"](new_value) self.write_settings() def apply_settings(self): if self.app.current_editor and self.app.current_editor.id == "code-editor": self.app.current_editor.soft_wrap = bool(int(self.get("editor", "word_wrap"))) self.app.theme = self.get("appearance", "colour_theme") def write_settings(self): with open(self.config_dir / "config.ini", "w") as configfile: self.config.write(configfile) def load_settings(self): if os.path.isfile(self.config_dir / "config.ini"): self.config.read(self.config_dir / "config.ini") if int(self.get("config", "version")) < 2: self.app.notify( message="Your config version is out of date! We've loaded the defaults but your config is untouched.", title="Config Version Outdated", severity="warning", timeout=15 ) else: return self.config["config"] = { "version": "2" } self.config["editor"] = { "word_wrap": "0", "line_numbers": "1", "default_editors": {}, "bindings": { "open": "ctrl+o", "open-folder": "ctrl+shift+o", "new": "ctrl+n", "save": "ctrl+s", "save-as": "ctrl+shift+s", "find": "ctrl+f", "settings": "ctrl+f1" } } self.config["plugins"] = { "enabled": "1", "log": "1", "log_timeout": "10" } self.config["appearance"] = { "colour_theme": "textual-dark" } with open(self.config_dir / "config.ini", "w") as configfile: self.config.write(configfile)