So recently I made this plugin that adds a bunch of stuff to a lanaguage called Gamemaker Language (GML). I tried to study making a plugin a bit but soon found it unnecessary to write any Python script in this plugin, and I only need to pack them all.
However, there’s one feature that I want to add, and I am wondering if this can be achieved without any Python as well?
Here’s the feature: using all folder names in an opened projects and treat them as keywords with syntax highlighting and auto-completion.
With the folder names being dynamic based on the project that’s open, I don’t think you’re going to get away Python-free on this one.
A plugin with an on_query_completions event listener could detect when auto-completion is happening and inject the folder names into the list of candidates. There is an example of this in the HTML package that ships with sublime in a file named html_completions.py (use PackageResourceViewer to get at the file) if you want an example of how that’s done.
The syntax highlighting part is stickier, though. The syntax highlighting comes from the syntax definition file (for your plugin, the tmLanguage file), and short of writing code that constantly rewrites that file so that it can provide rules to match the current project folders, I don’t think that’s actually possible.
For highlighting, you will have to resort to post-mortem highlighting using the View.add_regions api. This is what packages like SublimeLinter and BracketHighlighter use, if you know these.
ok I see what you’re saying. I checked that file and it’s quite complicated given I have no idea about the plugin API. I guess I have to learn it a bit to make that feature. Thank you.
So I managed to retrieve the folder names and mark them using the View.add_regions API. See:
I still have few questions:
Is there a way to inject scopes to regions? So instead of drawing boxes or underlines for the regions, I can actually recolor them?
Do I need to worry about the performance issue with my code? Currently, it recomputes folder names everytime on_query_completions is triggered. I need some kind of call-back function that refresh the folder names only when there’s a file change in the current project, but I couldn’t find such a function in the API.
When a list of completions is showed, some of them have a little explanation attached to them (showed in parathesis). Is this done by explicitly putting that in the trigger (but it’s somehow aligned to the right) or some other way?
Not as far as I am aware, no; the scopes get defined by the syntax and the syntax alone. I think the design decision in play is that the semantics of the language allow the syntax to say e.g. “This part of the text is a folder” and the regions API allows you to say “and this folder is special for some reason” by marking it.
Out of curiosity (I have used GameMaker in the past but it’s been a while) what’s the use case for only caring about certain folders and not just all folders? Aren’t all of them (or at least all of them rooted in one of the ones your plugin mentions) important?
Tricky question; everything performs well right up until it doesn’t and then all bets are off. For what it’s worth, AutoFileName does a scan every time completions are offered from the looks of things, so there is at least precedent.
At the moment you’re scanning for any completion anywhere inside of a gml file; if there are only certain places where you need to do this (e.g. inside of a string or something like that) you could further refine the scope so that it doesn’t happen, which would remove any queries of the folder list outside of those scopes.
Figuring out what is and is not a part of a project (and detecting when that changes) seems to be a missing piece in the API. I think for the time being you would have to figure it out on your own by interfacing with the OS (i.e. replicating whatever it is that Sublime is doing in this case).
Just as an aside on your code, project_path tells you the location of the current project file, which is not neccesarily inside of the path where the folders of the project are contained, and folder only tells you the first folder open in the project.
The sublime.Window.folders() method tells you the list of all currently open folders, whether there is a project currently in place or not, which may or may not be a better way to go.
Also don’t forget that the project can contain settings that tell sublime to exclude certain paths from the project, so in theory you would have to process that list yourself to make sure you don’t offer a folder that the user has excluded (assuming anyone cares or that is appropriate).
on_query_completions supports trigger based completions, so you can include extra text after the trigger text to get this kind of effect. For example:
Sorry for the late reply, got water damage on my computer
So I thought about the folder thing, and I agree it’s weird. The purpose of that was to read off names of user-created objects/sprites/sounds/etc since GameMaker: Studio creates folders based on the names. However, I figured a better way to do this is to parse system created file that contains such information. It’s more work since I not only walk through directories but also parsing files. Yet it’s definitely more intuitive.