4

也许这是另一轮“静态与实例”,但我试图在细节上成为魔鬼。请注意,我对 Python 和插件开发相当陌生。

一些插件使用以下代码:

s = sublime.load_settings(__name__ + '.sublime-settings')

class Settings:
  def load(self):
    self.setting1 = s.get('setting1', 'default1')
    self.setting2 = s.get('setting2', 'default2')
    ...

# Global Scope

settings = Settings()
settings.load()
s.add_on_change(__name__ + '-reload', settings.load)

这个例子没有什么特别的错误,除了load_settings方法add_on_change应该属于Settings

class Settings:
  def __init__(self):
    self.settings = sublime.load_settings(__name__ + '.sublime-settings')
    self.settings.add_on_change(__name__ + '-reload', self.setup)
    self.setup()

  def setup():
    self.setting1 = self.settings.get('setting1', 'default1')
    self.setting2 = self.settings.get('setting2', 'default2')
    ...

# Global Scope

settings = Settings()

现在Settings类封装了所有功能。但是我们真的需要这个类的实例吗?我不这么认为。这就是为什么static应该使用:

class Settings:
  settings = sublime.load_settings(__name__ + '.sublime-settings')

  @staticmethod
  def init():
    Settings.settings.add_on_change(__name__ + '-reload', Settings.setup)
    Settings.setup()

  @staticmethod
  def setup():
    Settings.setting1 = Settings.settings.get('setting1', 'default1')
    Settings.setting2 = Settings.settings.get('setting2', 'default2')
    ...

# Global Scope

Settings.init()

上述示例的优点(如果有的话)是:

  • 类封装了与设置相关的所有功能
  • 无需创建实例即可访问设置

还有其他“体面”的方式来组织设置吗?

4

2 回答 2

0

您的第二个示例是最好的方法,所有内容都封装在一个类中。这种@staticmethod方法没有我能想到的优势——拥有一个一次只能创建一个实例的类没有任何问题。

settings = Settings()应从内部调用全局分配plugin_loaded()以确保 ST API 已准备好,否则sublime.load_settings(FILENAME)可能会失败。

应该使用回调,以便如果用户更改设置 Sublime Text 不需要重新启动以更新设置的值。plugin_unloaded()如果用户卸载插件,这些应该被删除。

这是一个粗略的模板,取自我的一个插件。它应该作为一个很好的起点,可以将设置处理添加到 Sublime Text 插件中。

代码也在这个GitHub Gist中。

import sublime
import sublime_plugin

# The global scope ensures that the settings can
# be easily accessed from within all the classes.
global settings

def plugin_loaded():
    """ 
    This module level function is called on ST startup when the API is ready. 
    """

    global settings
    settings = Settings()
    settings.load_all()

def plugin_unloaded():
    """
    This module level function is called just before the plugin is unloaded.
    """

    settings.remove_callbacks()

class Settings:
    """
    Handles all the settings. A callback method is added for each setting, it 
    gets called by ST if that setting is changed in the settings file.
    """

    def __init__(self):

        FILENAME = "YourPluginName.sublime-settings"
        self.settings = sublime.load_settings(FILENAME)

        # User configurable settings.
        self.max_display_length = None
        self.monospace_font     = None
        self.ellipsis_symbols   = None
        self.prefix_custom      = None
        self.command_names      = None
        # ...

    def load_all(self):
        self.init_setting("max_display_length", self.set_max_display_length)
        self.init_setting("monospace_font", self.set_monospace_font)
        self.init_setting("ellipsis_symbols", self.set_ellipsis_symbols)
        self.init_setting("prefix_custom", self.set_prefix_custom)
        self.init_setting("command_names", self.set_command_names)
        # ...

    def init_setting(self, setting_name, setting_method):
        """
        Calls the setting_method to set the setting's value and registers the 
        setting_method as a callback so that it will be called by ST if the 
        setting's value is changed by the user.
        """

        setting_method()
        self.settings.add_on_change(setting_name, setting_method)

    def remove_callbacks(self):
        self.settings.clear_on_change("max_display_length")
        self.settings.clear_on_change("monospace_font")
        self.settings.clear_on_change("ellipsis_symbols")
        self.settings.clear_on_change("prefix_custom")
        self.settings.clear_on_change("command_names")
        # ...

    # Methods for the user configurable settings.

    def set_max_display_length(self):
        DEFAULT = 70
        MIN_LENGTH = 50
        value = self.integer_setting("max_display_length", DEFAULT, MIN_LENGTH)
        self.max_display_length = value

    def set_monospace_font(self):
        DEFAULT = True
        value = self.boolean_setting("monospace_font", DEFAULT)
        self.monospace_font = value

    def set_ellipsis_symbols(self):
        DEFAULT = "…"
        value = self.string_setting("ellipsis_symbols", DEFAULT)
        self.ellipsis_symbols = value

    def set_prefix_custom(self):
        DEFAULT = ""
        # This setting may be set to a string or a list of strings;
        # if it is a list then ensure all list elements are strings.
        value = self.list_or_string_setting("prefix_custom", DEFAULT)
        if isinstance(value, list):
            for index, item in enumerate(value):
                if not isinstance(item, str):
                    value[index] = str(item)
        self.prefix_custom = value

    def set_command_names(self):
        DEFAULT = []
        value = self.list_setting("command_names", DEFAULT)
        self.command_names = value

    # Methods for settings retrieval; all will return a
    # setting of the required type or the default value.

    def string_setting(self, setting, default):
        return self.setting_of_type(setting, default, str)

    def list_setting(self, setting, default):
        return self.setting_of_type(setting, default, list)

    def boolean_setting(self, setting, default):
        return self.setting_of_type(setting, default, bool)

    def list_or_string_setting(self, setting, default):
        return self.setting_of_type(setting, default, (str, list))

    def setting_of_type(self, setting, default, required_type):
        value = self.settings.get(setting, None)
        return value if isinstance(value, required_type) else default

    # Special case.

    def integer_setting(self, setting, default, min_value):
        value = self.settings.get(setting, None)
        return value if self.is_integer(value) and value >= min_value else default

    def is_integer(self, value):
        # Bool is a subclass of int; isinstance(False, int) == True.
        return isinstance(value, int) and not isinstance(value, bool)
于 2019-08-05T21:07:50.553 回答
-2

将插件设置放在Preferences.sublime-settings包根目录中命名的文件中。这些设置将由 Sublime Text 自动加载并合并到 Sublime 的默认设置中。然后,您可以通过 Sublime API 访问所有插件设置,而无需任何样板代码。

考虑以下名为的插件设置文件Preferences.sublime-settings

{
    "plugin_name_setting1": true,
    "plugin_name_setting2": true
}

这些设置可通过视图对象获得:

window = sublime.active_window()
view = window.active_view()
settings = view.settings()
setting1 = settings.get('plugin_name_setting1')
setting2 = settings.get('plugin_name_setting2')

例如,从 TextCommand 内部:

class ATextCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        setting1 = self.view.settings().get('plugin_name_setting1')
        setting2 = self.view.settings().get('plugin_name_setting2')

或从传递给函数的视图对象依赖项:

def my_plugin_func(view):
    setting1 = view.settings().get('plugin_name_setting1')
    setting2 = view.settings().get('plugin_name_setting2')

您还可以通过直接加载首选项文件来访问它们:

settings = sublime.load_settings('Preferences.sublime-settings')
setting1 = settings.get('plugin_name_setting1')
setting2 = settings.get('plugin_name_setting2')

优点

主要的好处是不需要样板代码。您只需创建设置文件并将其放入插件的根文件夹中。

用户可以全局配置您的插件,基于每个项目。

要全局设置设置,用户可以使用全局首选项文件,菜单 > 首选项 > 设置

{
    "plugin_name_setting1": true,
    "plugin_name_setting2": true
}

要将它们设置为项目前级别,他们可以使用他们的项目文件设置,菜单 > 项目 > 编辑项目

{
    "settings": {
        "plugin_name_setting1": true,
        "plugin_name_setting2": true
    }
}

这些设置也可以在键映射上下文约束中使用:

{ 
    "keys": [",", "a"], 
    "command": "a_plugin_command", 
    "context": [{ "key": "setting.plugin_name_setting2" } ] 
}

缺点

您的用户插件设置不在单独的命名设置文件中。如果您在所有插件设置前加上插件名称,我认为这没什么大不了的,这样它们将在首选项文件中彼此并排排序。我不知道其他的缺点是什么。

于 2017-04-29T16:44:25.280 回答