Sublime Forum

API Suggestions

#115

I also wanted to suggest an indentation system that is based on scopes (and the powerful syntax lexing that’s happening all the time anyway), but hold back onto creating an issue for it since I haven’t yet drafted out a specific suggestion on how I would imagine this feature.
I also wonder if it should be part of the syntax definition or utilize a separate scope-selector-oriented mechanism like tmPreferences do.

5 Likes

#116

Another thing that I’d like is improved parsing of the output panel.
For now errors have to be extracted through two regexes, one for the file and one for the line, character offset and message.

So here are 3 suggestions in increasing order of complexity.

  1. the output of the C# compiler doesn’t print full path to files, only the name of the file.
    This prevent the “next_result” command to open the correct file.
    Proposed fixed: if the matched path doesn’t exist try to open a file in the current project with the same name.

  2. Sometimes the error message is on different line than the line number (eg in Python). So there is no way to capture the error message.
    Proposed fix: add a third regex for the error message.

  3. There is no distinctions between errors and warnings. You have to choose when you create the build system if you want to catch both or only errors. Ideally the build system would provide a way to extract both, and the user could choose which one to display.

8 Likes

Dev Build 3118
Matching multiline build system output file_regex
#117

I hate it that I always find something to comment on in these threads, but I just can’t help it. If that’s not desired, I’ll continue replying to posts in new threads, but I’m on mobile atm.

To 1.: relative file paths are a possibility (and by default relative to the active view’s file’s directory. It can be configured with a setting that you can inspect from exec.py.

To 2.: you can capture the message in the regular expressions, but nothing is really done with it. I think it shows in the status bar on some action.

0 Likes

#118

If you capture the message in a build systems, it is shown in phantoms. I think there is a build-error tooltips package out there that also uses the messages. So, in 3118 this works as described here.

0 Likes

#119

I wasn’t aware of that so I updated my feature request.
The problem is that it must be one regex matching both line number and error message.
But for python where the error message is on a different line than the line number, you can’t capture it (confirmed by Jps in the build 3118 thread).

0 Likes

#120

Syntax-specific option for case-insensitive goto-definition

There was some discussion here:

Essentially, for languages that are case-insensitive (Fortran is the example I care about) it is desirable that goto-definition is case insensitive (e.g. you define MYFUNCTION but then call MyFunction, currently goto-definition doesn’t work for this case). This is already partly solved by the ability to perform a symbol transformation to convert all indexed symbols to lowercase, but goto-definition will then not work on any word that is not lowercase. I see at least three possible solutions:

  1. Add a caseInsensitive option to the tmPreferences. This works for case insensitive languages but is the least general approach. It has the advantage that the original capitalisation would be preserved in the symbol list.

  2. Apply symbolTransformation to the current word before looking it up in the index. This might break something else, I’m not sure.

  3. Add a lookupSymbolTransformation to the tmPreferences that would be applied to the current word before looking it up in the index.

Importance: major for case-insensitive languages

3 Likes

#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