Sublime Forum

API Suggestions

#121

Full set of character codes in minihtml

For arbitrary text, the python function html.escape often produces HTML codes that minihtml doesn’t understand, such as ' for ':

import html
view.add_phantom("id", sublime.Region(0,0), html.escape("'"), sublime.LAYOUT_BLOCK)

Obviously this makes it difficult to display arbitrary strings in Phantoms or Popups.

Importance: minor

Edit: It seems that minihtml currently works reliably with only the following substitutions (I have tested with all ASCII characters):

def to_html(s):
    s = s.replace('&', '&')
    s = s.replace('<', '&lt;')
    return s
6 Likes

#122

It would be nice to have “onpopupshowing” event on menus. And to allow programmable creation of the menuitems for that menu. This will allow:

  1. Infinite nested menus and menuitems with variable amount of menuitems or menus.
  2. avoid all processing that are shared in the menuitems, instead or processing stuff for each menuitem of a menu in a single command as we do now, you just do the processing on the onpopupshowing event and then reuse the calculations on all the menuitems that need it.
0 Likes

#123

API to get theme/scheme colours etc

Currenly, I have some code to get the popupCss background/foreground colours for the current view so that I can use these to generate an image that will not look out of place with the colour scheme. My current code looks like this (thanks to @FichteFoll for getting me started) :

    def parse_popupCss(css):
        words = css.split()
        i = 0
        bg = None
        fg = None
        while words[i] != "html":
            i += 1
        while words[i] != "}":
            if words[i] == "background-color:":
                bg = words[i+1]
            if words[i] == "color:":
                fg = words[i+1]
            i += 1
    
        # Defaults if not found
        if bg == None:
            bg = "#FFFFFF"
        if fg == None:
            fg = "#000000"
    
        # Remove leading # and trailing ;
        bg = bg[1:7]
        fg = fg[1:7]
    
        return bg, fg
    scheme_path = view.settings().get("color_scheme")
    scheme_content = sublime.load_binary_resource(scheme_path)
    scheme_data = plistlib.readPlistFromBytes(scheme_content)
    css = scheme_data["settings"][0]["settings"]["popupCss"]
    bg, fg = parse_popupCss(css)

This is not ideal:

  • finding, loading and parsing the PList again seems hacky, when Sublime presumably has it in memory already

  • the structure of the nested lists/dicts seems potentially fragile

    • scheme_data["settings"][0] is necessary to avoid searching through the entire list for the dict who’s only key is "settings", but is this always guaranteed to be the first in the list?
    • scheme_data["settings"][0]["settings"] doesn’t give the reader any clue about why I am doing this
  • some of the colours are in dicts, others are in dicts inside of dicts, and some are now in chunks of CSS code inside of dicts

  • Colour strings can be found in a variety of different formats (FFF vs #FFFFFF vs #FFFFFF00, for example)

  • There is no CSS parser that ships with Sublime (that I know of), so I had to implement my own basic one. It is likely to break, depending on how schemes write their CSS.

  • The colour I’m looking for may not even have been defined in the end, and the code above doesn’t even check for this but is already quite long

The situation with themes further complicates matters:

  • There are potentially multiple resources (i.e. if a user overrides a theme, or uses a theme addon) that have to be searched through in the right order to find the final colour or setting

  • The file format and dict structure are both different which requires different logic to handle

Finally, in trying to reproduce all of the internal logic that Sublime uses, we are likely to make mistakes and end up being inconsistent, leading to hard-to-track-down bugs.

Since all of this is already being done by Sublime, I would prefer to see an API exposing this functionality that looks something like this:

# Each of these returns a flat dict with a predefined set of keys, all set

# the main "settings" section of a scheme
d = view.appearance()
#     {'background': '#FFFFFF',
#      'caret': '#7C7C7C',
#      'foreground': '#000000',
#      'invisibles': '#B6B6B6',
#      'lineHighlight': '#EFFCA68F',
#      'selection': '#E3FC8D'}

# how text with the given scope string would be drawn in the current view
d = view.scope_appearance("some.combination.of.scopes", selection=False)
#     {'background': '#FFFFFF',
#      'foreground': '#000000',
#      'italic': false,
#      ...
#     }

# get CSS properties
d = view.html_appearance(where="popup", what="h3")
d = view.html_appearance(where="phantom", what="html.background")
#     {'background': '#FFFFFF',
#      'foreground': '#000000',
#      'fontsize': 10,
#      ...
#     }

# get theme properties
d = sublime.theme_appearance("tab", current=True)
#     {'background': '#FFFFFF',
#      'foreground': '#000000',
#      'height': 10,
#      ...
#     }

The exact API could probably be better, but the important things are

  • Consistent with Sublime’s internal representation of these things

  • Automatically selects the most appropriate resource to read from

  • Guarantees existence of values by substituting in the defaults

  • Consistent form for colour strings (maybe even a Color type?)

Importance: minor I suppose

5 Likes

#124

Getting the colors by scope is possible now. I’ve been meaning to take what I have and make it a dependency. Maybe I’ll do that in the next couple of days.

But with that said, an API would be cool for this.

3 Likes

#125

Do you mean using one of your packages, or in the base install? From what I understand you have implemented a lot of the stuff I talk about above in a package.

1 Like

#126

I extract the styles similar to your example for my ColorSchemeUnit package.

An API would be good.

The current relevant code is (needs optimising):

class Style(object):

    def __init__(self, view):
        self.view = view
        self.parse_color_scheme_file(self.view.settings().get('color_scheme'))

    def at_point(self, point):
        return self.get_styles_for_scope(self.view.scope_name(point).strip())

    def parse_color_scheme_file(self, color_scheme_file):
        color_scheme_content = sublime.load_resource(color_scheme_file)
        color_scheme_dict = plistlib.readPlistFromBytes(bytes(color_scheme_content, 'UTF-8'))
        self.selectors_in_scheme = color_scheme_dict['settings']

    def get_styles_for_scope(self, scope):
        styles = dict()

        for scheme_selector in self.selectors_in_scheme:
            if 'scope' not in scheme_selector:
                styles.update(scheme_selector['settings'])

        matched_style = {'settings': {}, 'score': 0}
        for scheme_selector in self.selectors_in_scheme:
            if 'scope' in scheme_selector:
                score = sublime.score_selector(scope, scheme_selector['scope'])
                if score:
                    if score >= matched_style['score']:
                        matched_style['score'] = score
                        matched_style['settings'].update(scheme_selector['settings'])

        styles.update(matched_style['settings'])

    return styles
0 Likes

#127

Yes. I have a couple of plugins that use some of the same color libs: ExportHtml, ScopeHunter, ColorHelper, and the mdpopups dependency. The idea is to create one dependency they use and make it available to anyone else as well. Though mdpopups will be harder move to a color lib dependency as it is a dependency itself, and Package Control can’t specify dependencies for dependencies. Getting people who use mdpopups to migrate to using that color dependency will be more slow as I have to give people time realize the change is occurring and adapt.

Anyways, the idea is to clean up the code and provide a more sane API. I would just bundle the various color stuff as a dependency lib. That way my plugins will just use a shared dependency instead of all shipping the same/similar libraries as they do now.

I would change unnecessary things like ColorSchemeMatcher.guess(view, pt, scope) to something like you suggested above ColorSchemeMatcher.guess(scope, selected=True) (which I like), and it would provide you with the a style object with colors, emphasis, transparent colors converted to non-transparent colors (since the Sublime CSS engine doesn’t really handle the color mixing stuff), etc. And you could then also request special colors like gutter, selection, foreground, and background directly like this: ColorSchemeMatcher.get_special_color('selectionForeground', simulated_transparency=True). Not sure if I care about including access to stuff like activeGuide etc. as I don’t find that stuff useful, but maybe.

I think I would also throw in the library to adjust the colors, brightness, saturation, etc. I would throw in the colorbox library that can generate color boxes for sublimes HTML content. Maybe even provide access to a call to convert a scheme to CSS using standard TextMate scopes. Stuff like that. Then if I add more, everyone using the lib would get access to those things too.


I’m not sure yet how I feel about implementing a CSS parser though. I have no problem with providing access to the CSS though. People could put anything in there. And you still don’t get what Sublime is providing under that layer. Luckily, in its currently implementation, you have to keep things simple. You can’t really specify div.class or .class1 .class2 (or you couldn’t last I checked – I don’t know if things changed). All you can really do right now is element and .class and .class1.class2. But if any of that changes, things break and then get more complicated to parse.

I don’t feel like it is practical to scan for everything in the CSS. The only time I really care about scanning for weird scopes in a scheme is when I am replicating a Sublime View in HTML, and I only need to that for creating code blocks from Sublime in HTML, and for something like ScopeHunter. I don’t really need to replicate CSS in my sub CSS for popups and phantoms; I should already get that stuff for free as long as I don’t override it. I don’t want to scan for available classes either. I just want to know what standard classes are reasonably expected to be there. Whatever non-standard class you are looking for could be missing if you install another scheme. From a plugin standpoint, I would only be interested in what standard class names I can use, and maybe the body element back-ground color for when I insert images. I would prefer that themes keep that stuff simple so I don’t have to do elaborate analysis.

The reason why I wrote mdpopups was to standardize everything for myself and the plugins I write to spare myself from analyzing scopes in every plugin for popups; it’s all done for me. I just give it scopes and don’t worry about. If I need something specific, I inject a little additional CSS using known scopes. I know I can only expect standard TextMate scopes and .foreground and .background. I know that mdpopups will make the background and foreground color the same as the scheme background and the foreground. So it is a simple list I can reference. I now know that schemes are likely to include colors and font styling under the classes .error, .deleted, .success, .inserted, .warning, and .modified. I don’t care if a theme inserts other classes as those will vary from theme to theme and can’t be relied on. I am only interested in standard colors. I’d rather give up a little control to have things consistent.

2 Likes

#128

See also https://github.com/SublimeTextIssues/Core/issues/134

1 Like

#129

scrolloff and sidescrolloff similar to vim

						*'scrolloff'* *'so'*
'scrolloff' 'so'	number	(default 0)
			global
			{not in Vi}
	Minimal number of screen lines to keep above and below the cursor.
	This will make some context visible around where you are working.  If
	you set it to a very large value (999) the cursor line will always be
	in the middle of the window (except at the start or end of the file or
	when long lines wrap).
	For scrolling horizontally see 'sidescrolloff'.
	NOTE: This option is set to 0 when 'compatible' is set.

scrolloff vimdocs

						*'sidescrolloff'* *'siso'*
'sidescrolloff' 'siso'	number (default 0)
			global
			{not in Vi}
	The minimal number of screen columns to keep to the left and to the
	right of the cursor if 'nowrap' is set.  Setting this option to a
	value greater than 0 while having |'sidescroll'| also at a non-zero
	value makes some context visible in the line you are scrolling in
	horizontally (except at beginning of the line).  Setting this option
	to a large value (like 999) has the effect of keeping the cursor
	horizontally centered in the window, as long as one does not come too
	close to the beginning of the line.
	NOTE: This option is set to 0 when 'compatible' is set.

	Example: Try this together with 'sidescroll' and 'listchars' as
		 in the following example to never allow the cursor to move
		 onto the "extends" character:

		 :set nowrap sidescroll=1 listchars=extends:>,precedes:<
		 :set sidescrolloff=1

sidescrolloff vimdocs

4 Likes

#130

In addition to this, it would be great if add_regions() properly supported making text bold or italic as specified by the “fontStyle” setting in color schemes.

An alternative to this might be just to provide some way to programmatically change the scope of any region of text. Given the fact that add_regions() doesn’t actually change scope and its weird behavior when either the background or both foreground and background of a scoped setting in a color scheme match those of the global setting or are absent, this could definitely prove a useful addition, as text colored by the scopes specified by syntax files don’t suffer from these problems.

Also, it seems others have expressed interest in such a feature.

3 Likes

#131

In the View API there is:

find_by_class(point, forward, classes, <separators>) that “finds the next location after point that matches the given classes.” where classes can detect word/line start/end and punctuation.

I’d like a similar API with scope selectors instead of classes.

There is find_by_selector(selector) but it returns the list of all the matching regions in the file, while I only need the next one matching.

3 Likes

#132

It would be super nice to expose the dictionary related stuff, so we can spell check many languages at the same time in a single document

6 Likes

#133

Just to extend this a bit further, how about the ability to to also change the line color in the minimap, also like in Atom?

0 Likes

#134

A save command without side-effects

It would be nice to have a save command that does not produce any side effects, but only saves the view/buffer to file.

Currently, the save command is customized for for the use case where the user asks for the file to be saved, typically by pressing cmd+s (or ctrl+s). However, this action has side effects other than just saving the file. Most prominently, the save command will close auto-complete panels.

The problem: While the side-effects make sense when save is requested by the user, they make life harder for plugin developers who would like to save the file without closing auto-complete panels (or invoking other side effects). The auto-save package is one example of a plugin that has had to develop overly-complex solutions to work around the side effects of the save command.

The solution: Add the ability in the API to save the buffer without invoking side effects. It doesn’t matter too much exactly how this is done - whether via a completely new command, or through a keyword argument to the existing save command.

Importance: Minor (but also simple to implement…)

Notes (based on other comments):

  • Writing directly to file without using ST’s API doesn’t work well, since users get “File has changed on disk” messages every time the file is auto-saved (depending on their configuration). Doing things “the right way” means saving the view/buffer using Sublime’s API.
1 Like

#135

Why not just open and write to a file handle using python?

0 Likes

#136

Make More Types of Overlay Available to Plugin Developers

Importance: Major

Sublime Text has 5 distinct types of overlays, each with slightly different styles and capabilities. They are:

  • Command Palette
  • Show Files
  • Goto ‘Anything’ : Symbol / Line Number / Term
  • Goto Symbol In Project
  • Quick Switch Project

I propose that all of the styles and capabilities of these overlays be made available to plugin developers, either via the existing API Window class show_quick_panel() method or by the addition of a variety of show_xxx_panel() methods.

This would be a wonderful addition to the API, please consider it.

Proposed Overlay Additions To The API

  • A Two Column Overlay; like Command Palette

  • A Free Standing Overlay; like Quick Switch Project

    • The overlay is displayed in its own custom centered ‘dialog’ box / window
    • The overlay keeps the focus until a row is selected or it is cancelled
  • Flags to control the Width of the overlay

    • Auto-Expand Width; show_quick_panel() bug fix needed, not working properly with 80% of themes
    • Wide Width; like Command Palette, Show Files, and Goto ‘Anything’
    • Medium Width; like Goto Symbol In Project
    • Narrow Width; e.g. for showing a list of numbers, etc.
    • Extra Wide Width; e.g. for showing long paths at regular font size
    • If not enough room to show the text, width should auto-expand
    • Possibly mutually exclusive flags: AUTO_EXPAND_WIDTH and TRUNCATE_TEXT_AT_WIDTH_LIMIT
  • Flags to control the Font Size

    • Regular Font Size; like Command Palette, Show Files, and Switch Project
    • Small Font Size to allow more visible rows; like Goto ‘Anything’
  • A flag to set NO initially selected row; like Show Files

    • Or achieve this implicitly by setting selected_index to -1
  • A flag to prevent ST from stripping trailing whitespace

    • To allow indentation/padding to be added to the right side of rows as well as on the left
    • Alternatively indentation flags to achieve the same thing
    • So that the same style as Show Files indentation can be achieved
  • A method parameter to set the text in the overlay’s input box; like Goto ‘Anything’, and Command Palette

    • The show_input_panel() API method does this with its initial_text parameter
    • No initial text if set to an "" empty string
    • initial_text should be given to the awesome ST fuzzy matching so that only matching rows get displayed
    • Flag to make initial_text selected; like Command Palette does when it shows the most recently used text

Have I missed anything that others would like?

Many thanks.

4 Likes

Dev Build 3154
#137

Why not just open and write to a file handle using python?

Because if you write the file to disk manually then users get repeated “File has changed on disk” messages (depending on their configuration). Doing things “the right way” means saving the view/buffer using Sublime’s API.

0 Likes

#138

Point taken

0 Likes

#139

Clear the console.

sublime.clear_console()

I know it’s not really important, but when you create plugin, it would help to keep a scrollbar larger than 1px :grinning:

Matt

10 Likes

#140

Hello @wbond,

I am working on a search plugin. For reference the UX is currently defined like this plugin: https://github.com/krasun/SublimeInternetSearch

In order to make a search I have to open the input with show_input_panel then make a request and finally populate search results with the show_quick_panel API.
From a user perspective, they are searching in the input (bottom left of the screen) and then getting results in the middle. With a field to further filter results but that doesn’t work for new searches.
That makes the user flow very clunky.

Would be awesome if we could have the same callback that we have on the input_panel with the show_quick_panel with access to the user input and the ability to modify the list on user input (bypass the match).

In a summary I would love:

  • Access to the user input of the quick_panel
  • Bypass the match of that field

Importance: Major
Difficulty: Should be pretty easy for you.

Thank you !

3 Likes