sublime_text icon indicating copy to clipboard operation
sublime_text copied to clipboard

Allow providing color codes directly to `View.add_regions` method

Open jfcherng opened this issue 1 year ago • 3 comments

Problem description

Some plugins about "highlighting" have to set colors for sublime.Regions. E.g., https://github.com/jfcherng-sublime/ST-RainbowIndent

Currently, the only way to set foreground/background color for a sublime.Region via plugin APIs is View.add_regions. That API requires a scope, which cannot be created by the plugin itself decently. That means end users have to add color scheme rules manually. Furthormore, from then on, they have to maintain their own color scheme file.

Imho, average Sublime Text users would consider this flow cumbersome. They may expect they can directly specify color codes they want to used in the plugin settings.

Preferred solution

Allow providing color codes directly to View.add_regions method. By "color codes", I mean all these forms: https://www.sublimetext.com/docs/color_schemes.html#colors

Probably just let the existing scope argument accepts a dict-like color rule. E.g.,

view.add_regions(
    scope={
        # same with a color rule in a color scheme but without `scope`
        "foreground": "rgba(229, 57, 53, 0.35)",
        "background": "rgba(229, 57, 53, 0.2)",
    },
    # ... other arguments ...
)

Alternatives

Allow plugins to manipulate in-memory color scheme rules so plugin authors can inject their own.

Additional Information

No response

jfcherng avatar May 15 '24 05:05 jfcherng

Providing hardcoded color codes via plugins seems very likely to not play well with all color schemes at the same time. That's a step backward to how Notepad++ handles custom colors, IMHO, by disrupting the scope name abstraction layer.

Only a color scheme can decide how to tint all the entities to look aesthetic.

It is not the job of arbritary plugins to hack color schemes they don't know anything about.

Of course color schemes need to support relevant scope names, which might be a bit tricky with regards to maintanance state of most of them.

That's how colors for GitGutter, SublimeLinter and other plugins, which add regions is handled since ever.

deathaxe avatar May 15 '24 06:05 deathaxe

Providing hardcoded color codes via plugins seems very likely to not play well with all color schemes at the same time.

I don't hardcode anything. Color codes are provided by the user. This proposal is like "allowing defining color rules in plugin settings to be used in View.add_regions". That is, equivalently, the user can write plugin-specific color rules in plugin settings rather than must be in the color scheme.

This proposal asks for a new option. The user can still use the old flow if he considers the old way is better.

That's how colors for GitGutter, SublimeLinter and other plugins, which add regions is handled since ever.

Yes, and hence this proposal.

jfcherng avatar May 15 '24 07:05 jfcherng

I somehow like the simplicity of the proposal; maybe that's short sighted though.

Only a color scheme can decide how to tint all the entities to look aesthetic.

Ultimately the user decides which color they like. For a lot of regions we want to emphasize/mark there are no good and common semantics. No color scheme provides good "alternative" tokens/scopes.

From my understanding, the region.* scopes wanted to solve this or a very similar problem but they look unpleasant in basically every advanced context I tried them: a) often enough they don't look like the color its name suggests, b) especially the background/foreground color choices seem odd, c) they still lack the ability to style just the foreground.

E.g. this is what "bluish" (view.add_regions("test", [view.sel()[0]], scope="region.bluish")) looks like on my computer:

image

Basically, it lacks every test: it's not bluish, the contrast is poor, the colors are not vibrant.

E.g.

image

On the left yellowish, on the right hand string which is actually the yellowish color in my color-scheme. (This is with flags 512|256, just imagine we also had a flag REVERSED_COLORS which just flips the already computed back- and foreground colors. 🤷‍♀️.)

Alternative/variant

If we wanted to use existing scopes but still need the ad-hoc way of adding color variants, emphasizers, we would need color functions to adjust the style of a given scheme. E.g. if we know that the scope constant looks right and is used in the view, then we would need to express brighter(constant) or inverse(constant) etc. (Yes, these are typical color functions, and we have them within the scheme files; but here we would need to apply them on defined scopes.) E.g.

image

The current line has black dot on yellow, and the path to its children has a yellow on black styling. (The "black" is the background color.) These inverse variants are often very useful to highlight an area. The yellow is in fact a variant of the string.other scope. In this example: inverse(string.other) is used for the current line's dot, and fg(string.other) for the path/ascii art. We all know that the simple style-the-fg-color feature is still missing, e.g. flags=DRAW_WITH_REVERSED_COLORS. Rel #817

kaste avatar May 16 '24 09:05 kaste

No news? In my plugin, I ended up being forced to create a custom color scheme selector and ship the plugin with several pre-made styles.

https://github.com/Destro-/AMXXEditorV4/tree/main/styles/editor

Destro- avatar Oct 08 '24 13:10 Destro-

From my understanding, the region.* scopes wanted to solve this or a very similar problem but they look unpleasant in basically every advanced context

That's caused by most color schemes not specifying them, which causes ST to guess "proper" color values.

The experience wouldn't be much different for hard coded colors added by plugins. It would be very likely for them to look good only in few color schemes considdered by plugin authors. Finally plugin specific preferences would be added to provide customization possibilities, yeah!

A starting point would be to add valid -...ish colors to existing color schemes. Maybe unlikely to happen anytime soon.

... if we know that the scope constant looks right ...

It may work in certain use cases but I don't think that's a universally valid assumption, because color schemes can tint any entity arbritarily. It may be a fit in color schmee A, but an epic failure in B.

create a custom color scheme selector and ship the plugin

The strategy of providing color scheme overrides for known color schemes is, IMHO not the worst choice. Plugins may also provide snippets to be added to existing color schemes, so users can add missing rules easily to their own color scheme overrides, if desired. Maybe also provide mechanism to add default rules to the active color scheme, if no suitable rule was found.

The one way or the other - end users need a chance to customize those colors, depending on the color scheme they use.

deathaxe avatar Oct 08 '24 15:10 deathaxe

Fair points. I think my reasoning was that e.g. special views, like a GitSavvy view, or an error/diagnostics panel, always use more or less a set of arbitrary scopes, that somehow look coherent together. (Usually we briefly test this with the standard schemes.) E.g. GitSavvy assigns constant.other.git-savvy.sha1 to this commit hash

image

So this is some orangish, and of course reversed(constant.other) would work as a highlighting color. Generally speaking, reversed(keyword) or brighter(constant) would be purely descriptive, in that Sublime had to decide which color that actually means. A bit like a region.orangish when it is not defined, which it is on a lot of schemes, but in this case with a color-hint that works.

In contrast, defining scheme overrides, appendices to it, and snippets in a README, would be very concrete in that the user had to explicitly decide which color to use. That is also cool, but it is not descriptive, and it begs the question why we can't just do that in-memory, on the fly. (Like proposed in this issue.) Typically, in the README it says, "btw. we're using scope x here, you can define what that actually means in your scheme file", where you then define the mapping "x -> colorset". These names can be completely arbitrary, e.g. in GitSavvy there is a git_savvy.graph.dot.

Why not set this mapping in-memory, like register_color("git_savvy.graph.dot", {...}) which has the same ad-hoc'ness as proposed here, it would be just a different API. What is the benefit of the indirection when going thru a scheme file?

kaste avatar Oct 08 '24 18:10 kaste

One of the issues is that add_regions is broken. When using common scopes, the colors are always inverted. That's why we need to create a custom rule that adjusts the background color slightly to prevent the inversion from happening.

Destro- avatar Oct 09 '24 22:10 Destro-