Search code examples
apisublimetext3sublime-text-plugin

Using the Sublime Text 3 API, how would one customize a tab's design?


I'm creating a sublime plugin that would do something similar for windows as does TerminalView for MacOS / Linux.

Before I start, I am not here for any suggestions about such plugins. I decided I would like to create my own for several reasons and am (at least currently) not interested in what other people did.

The plugin opens a tab, reads any text typed in as input, pipes it to stdin, then read stdout and inserts the text into the tab. There is still a lot of work to go, but to add a finish to the first "iteration" - if you like, I attempted to get the tab to look less like a sublime text tab and more like windows' terminal - CMD.

Needless to say I failed miserably and am here for your help.

So, on a basic level, does anyone know how I can do the following via the sublime API ( via plugin code ):

  • Change color of the text.
  • Change background color of the tab ONLY, not including the rest of the screen.

TerminalView has done this pretty well. Here is a screenshot from there page explaining what I ultimately want to achieve:

What I'm trying to achieve

I would also like to say that I've looked pretty thoroughly through the API linked above, and also searched all over google, but I didn't come accross anything useful.

Does anyone know a function I can use or maybe a property that I can set to change color of text and color of background of a tab?

All input is much appreciated (except other plugin suggestions) :)


Solution

  • Something to keep in mind is that plugin development in Sublime is easier when you don't do it in a vacuum. That is to say, even if you're not interested in using someone else's solution to a problem (I often develop my own plugins rather than using an existing one, for example), looking at how someone else accomplished something similar to what you're trying to do is an invaluable learning tool.

    As such, I would recommend looking not only at the TerminalView package that you mentioned, but also Terminus (which is similar but works on all platforms). Even if you're not going to use them, you can examine their code to see how they work.

    That said, generally speaking, the color that text is rendered as is controlled by the color scheme that's in use in combination with the syntax definition that's applied to that particular file. The syntax uses rules to apply one or more scopes to every character in the file, and the color scheme uses rules that match those scopes to know what color to render them as.

    For example, the Python.sublime-syntax file contains a rule that recognizes the word def as defining a function and applies a scope of storage.type.function.python to it. The Monokai.sublime-color-scheme file includes a rule that says that things that match storage.type.function should be displayed as italicized blue text. Thus, if you use that color scheme and edit a Python file, the word def appears blue and italicized.

    Such functionality is not entirely useful for the purposes of a terminal plugin since the rules have to be able to explicitly match known text but a terminal can theoretically display anything you want. So this is not the way to go, or at least not entirely the way to go; the TerminalView package you mentioned in your original question allows you to apply a syntax if you want to, but still uses the following method overall.

    Programatically, you can use the view.add_regions() API endpoint to do something similar to this, with some caveats as we'll see in just a moment.

    You may have seen the effects of this API function even if you didn't know it. It can apply icons in the gutter associated with each line as well as applying graphical markup in the form of outlines, underlines, etc to text in the buffer.

    For example, a linter might include a yellow circle icon in the gutter to show you lines with issues and use a colored outline around text to show you where the problem is.

    One of the things it can be used for is to apply a colored region to text. In fact this is what both TerminalView and Terminus do to achieve this particular feature in their terminals. The region colors that it can apply are still controlled by the color scheme in use, however. So unless you want to be limited to only colors that exist in your current color scheme, you need to provide a custom color scheme to be used by your plugin as well.

    By way of example, create a file named Test.sublime-color-scheme in your User package with the following contents:

    {
        "globals":
        {
            "foreground": "white",
            "background": "#000001",
            "caret": "white",
        },
        "rules":
        [
            {
                "name": "Green on Black",
                "scope": "terminal.green.black",
                "foreground": "green",
                "background": "black"
            },
            {
                "name": "Black on Green",
                "scope": "terminal.black.green",
                "foreground": "black",
                "background": "green"
            }
        ]
    }
    

    This is a very minimal color scheme that sets the overall text color to white and the background color to almost but not quite black (for reasons we'll cover in a bit). It also includes two rules, one that knows how to color text as green on a black background and one that knows how to color text as black on a green background.

    With this in place, create a new empty view and use the lorem snippet to fill it with some sample text, then use the Sublime console to run the command view.settings().set("color_scheme", "Test.sublime-color-scheme"), You should see the background of file change to black (along with the tab) while the text remains white.

    Now, back in the console, execute the following commands:

    view.add_regions("green_black", [sublime.Region(163, 175)], "terminal.green.black")
    view.add_regions("black_green", [sublime.Region(184, 191)], "terminal.black.green")
    

    The results of that are a file that looks like the following:

    Sample region image

    What's happening here is that the text in the first region is being colored by the scope terminal.green.black, which makes the exercitation text appear as green text on a black background. The second scope similarly makes the laboris text black on a green background.

    It's important to point out that the overall background color is almost, but not quite, black. The reason for this is that if you apply a region with the same background color as the overall background color, it will invert the foreground and background colors to apply contrast.

    The result of that would be both of these examples appearing as black text on a green background. By having the overall background color be slightly different (but still virtually indistinguishable from black), the colors you provide are applied directly.

    A more complete example would require you to have that example color scheme have every possible combination of foreground and background colors that you're interested in so that you have more scopes to work with.

    Similarly, your code would need to apply regions anywhere the text color is not the default color. The first argument to add_regions() is a unique key that is used to reference the regions in the list that it's coloring. As such it's also important to use a different region name for every possible color at a minimun; if you reuse the same key, the list of regions will change.

    Regarding the second part of your question, as seen above the color of the physical tab as well as the area that represents the file are controlled by the background property of the color scheme in use in that particular view. So in this example, regardless of the color scheme you normally use, the background color of the "terminal tab" will always be black.

    It's unclear from how you stated the question (and from your clarification in the comments) whether you meant the physical tab (named Terminal (bash) in your screenshot above) or the contents of the tab (i.e. the shell output).

    As far as I'm aware, it's not possible to make the physical tab a different color than the content of the tab; they always remain linked together.-