Sublime Forum

Is it possible to implement a per-view setting?

#1

For example, my plugin has a mode setting which can be set to three valid value mode1, mode2, and mode3.

The user can set mode to mode1 for view1 and set mode2 for view2. In other words, I want to implement a per-view setting. Sublime’s syntax Combobox is a good example. However, adding custom Combobox to the status bar is impossible. I have tried the checkbox menu but there are not mutually exclusive. Does anyone have some ideas to do this? Many Thanks!

0 Likes

#2

Indeed view specific settings are possible; the Settings object attached to a view is available via the view.settings() API endpoint; you can apply any settings you want to that settings object and they’ll apply to only that particular view (if you choose a setting that already exists elsewhere, then it overrides what the default would be). The settings of a view are persisted in the session/project, so they even retain their values over a restart or close/save of a project, assuming you have settings set as appropriate.

import sublime
import sublime_plugin


class ExampleCommand(sublime_plugin.TextCommand):
    def run(self, edit, mode=None):
        if mode:
            self.view.settings().set("mode", mode)

        sublime.message_dialog(self.view.settings().get("mode", "Unset"))

Every time you run this command, it uses a dialog to tell you what the mode setting is set to; if you also provide a mode argument, it will set that setting in as well.

The API for the status bar allows you to display text in it, but you can’t interact with it (as you’ve already discovered); so you could display what the setting is currently set to, but you can’t use that to modify it. You can however add items to the main menu or context menu that do this.

For example, given this Context.sublime-menu in your package:

[
    { "caption": "-"  },
    { "caption": "Mode 1", "command": "example", "args": {"mode": "mode1"}, "checkbox": true },
    { "caption": "Mode 2", "command": "example", "args": {"mode": "mode2"}, "checkbox": true },
    { "caption": "Mode 3", "command": "example", "args": {"mode": "mode3"}, "checkbox": true },
    { "caption": "-"  },
]

The context menu would have three items which set the setting to one of the three modes; you also need to augment the plugin with an additional is_checked() method, which Sublime will invoke when displaying the menu to see if it should be checked or not. Sublime calls the method with the argument that would be given to the command when it’s picked:

    def is_checked(self, mode=None):
        if mode:
            return True if self.view.settings().get("mode") == mode else False

        return False

The Settings.get() method takes a second argument that represents the value for the setting if it’s not set yet; above that’s being passed as "Unset" as an indication that the mode is not set yet, but you could replace that with something like "mode1" in all of the calls where you retrieve the setting so that there’s a default, unless you already take care to set the setting somewhere else first (or it doesn’t matter).

0 Likes

#3

@OdatNurd Hi, Many Thanks! This way is indeed what I need.
The is_checked() method is pretty useful although the official documentation doesn’t explain it clearly. I once thought only ApplicationCommand class has is_checked() method and failed to figure out how to associate with the View object. I have debugged your snippet and finally understand all of that. Besides, I find some unofficial documentation are good supplements for the official API list.
Thanks again!

0 Likes

#4

Actually if you inspect the sublime_plugin.py file, you can see that TextCommand, WindowCommand etc inherit from a base Command class which has the is_checked method defined. So that method becomes available to both TextCommand and WindowCommand even though it isn’t documented anywhere as far as I can tell.

0 Likes

#5

@UltraInstinct05 Reading the source code is truly a nice way. You provide a very clear explanation. Thanks!

0 Likes