While some developers are married to their editor of choice, I’m a little radical in that I prefer to keep a lover on the side 🌶️
I use two editors for work purposes – VS Code and Sublime Text. Part of the reason why is that I started with Sublime and laid out a solid network of keyboard shortcuts that are magnificently efficient for standard processes.
What can I say? Habits are bloody hard to break.
Regardless, I’m a very big proponent of VS Code hence the reason I created a custom theme.
I spend hours on VS Code coding custom applications and creating projects for stuff I’m learning.
Hours that I don’t spend on Sublime. In fact, I don’t even have Sublime downloaded on my personal laptop 😬
But go on my work laptop and Sublime is prominently displayed on my Dock, right next to my dear VS Code.
Why choose custom autocomplete?
It just so happened that I have a particular process for work with lots of standard repeats that can be improved upon by a simple autocomplete functionality.
I’m talking about custom autocomplete options that would be triggered by specific keywords and place a desired custom value on the editor.
Luckily, Sublime Text has a way for us to add custom select lists in what it calls Completions.
In fact, there’s more than one way – you can choose from completion files, snippets, and plugins.
I opted for a plugin because, despite being the most powerful tool for adding custom completions, I found it the easiest to implement (aka write it once, use it always).
In this post, I’ll create a custom autocomplete plugin for Sublime Text and use the autocomplete options on HTML files.
What this post won’t do is go into in-depth knowledge of Sublime plugin development or a how-to on Python.
Hey!
Even though Sublime plugins are written in Python, you don’t need to have a Python background to follow along. This is time-saving, surface-level implementation, though, you have my blessings on delving deep into the topic on your own!
Custom completion code for Sublime
With the guidance of OdatNurd’s Sublime Text Tutorials, I’ll customize a Sublime autocomplete with the on_query_completions
event by using a list of trigger text to insert custom snippets as replacements.
Start by creating a file with a py extension, I’ll call it example_completions.py.
The basic skeleton code for the autocomplete plugin is:
import sublime
import sublime_plugin
class MyCompletions(sublime_plugin.EventListener):
def on_query_completions(self, view, prefix, locations):
if not all(view.match_selector(pt, << VIEW SCOPE >>) for pt in locations):
return None
return << SOMETHING, WHETHER LIST OR TUPLE >>
Ultimately, what the above code does is:
- use built-in Sublime methods to create plugins
- define a custom class of select list items
- check the view scope in which to implement the autocomplete
- return the custom select list
In my case, the view scope is source.html
because I’d like the custom autocomplete options to appear only on my HTML files.
And it makes sense to have different autocompletes for different file types because different languages have different requirements.
Don’t you just love how often “different” came into that sentence? 😅
Going back to the code, I’d like to return a custom list of options for adding specific attributes and styles to tables.
Scenario
My objective is to clean up an HTML file by formatting certain elements based on specific requirements.
One of such requirements is to validate tables by:
- including appropriate scopes for column and row headers
- adding specific styles based on table header tiers
- inserting a table caption where necessary
Filling out the skeleton code above looks like this:
import sublime
import sublime_plugin
class MyCompletions(sublime_plugin.EventListener):
def on_query_completions(self, view, prefix, locations):
if not all(view.match_selector(pt, "text.html") for pt in locations):
return None
return [
["col\Column scope table attribute", "scope='col'"],
["row\Row scope table attribute", "scope='row'"],
["colgroup\Column group scope table attribute", "colspan='100%' scope='colgroup'"],
["double\Double header table styles", "class='double-head-tbl'"],
["merged\Merged header table styles", "class='merged-head-tbl'"],
["cap\Insert small text caption", "<small>$0</small>"]
]
Setting the view scope
Take a look at how the previous placeholder, << VIEW SCOPE >>
, is now "text.html"
.
To get the view scope of your file:
- Open the file for which you want to get the view scope
- Navigate to Tools >> Developer >> Show Scope Name
Then move toward the return statement of the custom list options.
First, notice how MyCompletions
returns a list of lists.
Second, note each list contains two strings, the first being the trigger while the second is the replacement item.
Now, the trigger found in that first string contains two parts separated by a backslash character. The left part is the actual trigger term whereas the right part is a description of what that trigger will do.
In other words, "col\Column scope table attribute"
means that typing “col” will insert a scope attribute with a value of “col” (something typical in HTML tables that are optimized for accessibility).
The trigger “col” in itself will insert scope='col'
which is exactly what I want to see on my table!
Tip ✨
You can test out your custom triggers right on the python file if you use the view scope for that file (hint:"source.python"
).
Adding a placeholder
Moving on, let’s address one more odd aspect and that’s the strange $0
found in the replacement string for the “cap” trigger.
Based on the description, “cap” inserts a caption and that basically is some text wrapped in the HTML small tag.
This may appear redundant because Sublime is equipped with HTML file autocompletes that includes <small>
. But it’s a nice simple example to illustrate the use of a placeholder so let’s roll with it.
Typing “cap” and tabbing over will insert the small tags and place the cursor where the placeholder character is located at.
Simple as that.
Note: The dollar sign,
$
, is a reserved character in the python file. If you want to insert an actual dollar sign character, make sure to escape it with a backslash!
Where to save custom completions
So far, we’ve created a Python file called example_completions.py (or whatever you called yours) and defined a custom class that adds certain autocomplete options.
Question for ya: Where did you save the file?
The location of the file matters. In order for your custom completion plugin to work, you need to place this file inside the User directory in the Packages directory for Sublime.
Did you catch that? 😵💫
An easy way to get to the Packages directory is by navigating to Sublime Text >> Settings… >> Browse Packages…
Then from there, select the User folder. Take your Python file and throw it in there (in a very digital sense of throw).
If you’re skeptical like me and want to check that Sublime is correctly picking up your custom class, there’s a way to do that too.
Head to Tools >> Developer >> Profile Plugins to pull up a Plugin Event Profile file that contains a list of event handlers and the respective events that Sublime is listening to.
Under the on_query_completions
event, you should find our custom file’s name.
How to use a custom select list in Sublime
Time to put everything to the ultimate test 🥁
In my HTML file, I’ll test out all my triggers like so:
Double head trigger
Merged head trigger
Caption trigger
Tip: If you like using double quotes on element attributes, define the trigger values with single quotes. Likewise, escape the quote characters appropriately to get your desired syntax.
With this, we’ve arrived at the end of this little side project that increases efficiency and makes our developer lives easier.
Remember that you can always make the autocomplete customizations far more intricate to suit your needs!
💡 For example, you can create a list of completions that you then loop over to create each child list item. Append each item in an empty list and return that instead.
Don’t be limited in the scope of this simple example. I, certainly, use the more complex alternative for my job functions (which shall not be shared ‘cause I still need a job LOL).
However, no matter how complex you get, the gist of it is summed here rather well.
See ya at the next rodeo 🤠