Making a Sublime Text Plugin: Markdown Reference Viewer

August 27, 2012 by Gabe | [mmd] |

I've been tinkering with converting some of my BBEdit customizations into Sublime Text macro, snippets and plugins. Making a Sublime Text plugin requires some knowledge that I found difficult to acquire in one place. So, I'm putting it here for someone else to benefit from.

Reference Viewer

When I write a long post, I generally gather some reference links before I start writing in earnest. As I write, I intertwine the references with the content. This works well for me except for the cumbersome scrolling and split screening so I can remind myself what the references are.

I decided I needed a plugin to show me the links and then insert the one I want. This is similar to something I created for BBEdit. Besides, I couldn't let Dr. Drang have all the fun.1

I bound this plugin to ⌘+alt+ctrl+l. When triggered, it displays the Sublime Text quick panel with a list of the existing reference style link. Selecting a link inserts the reference marker at the current selection.

Sublime Plugins

Plugins for Sublime Text are written in Python using the Sublime Text API. There are some very nice API calls that can be used to bend Sublime Text quite a bit. The documentation is well written.

I found debugging a little tedious. To get a bit more information about what's going on, I turned on the console (control+`) and then enabled command logging by running the following command at the Sublime Text console:

sublime.log_commands(True)

Once this is enabled, every action that is executed is output to the console.

The Code

The plugin is rather simple. If you are familiar with Python then you need little explanation.2 I'll run through the Sublime Text specific features.

import sublime, sublime_plugin

class ListLinksCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        self.markers = []
        self.view.find_all("(^\\[.*?\\]): (http(s?):\\/\\/.*)", 0, "$1: $2", self.markers)
        self.view.window().show_quick_panel(self.markers, self.insert_link, sublime.MONOSPACE_FONT)

    def insert_link(self, choice):
        if choice == -1:
            return
        edit = self.view.begin_edit()
        startloc = self.view.sel()[-1].end()
        link = self.markers[choice].split(':')[0]
        self.view.insert(edit, startloc, link)
        self.view.end_edit(edit)

Here's what's going on. The following line performs a regular expression search on all of the content of the current view and returns a hit list for anything that looks like an end reference [ref_name]: http://someurl.com.

self.view.find_all("(^\\[.*?\\]): (http(s?):\\/\\/.*)", 0, "$1: $2", self.markers)

The list will look like:

[ref1]: http://myurl.com
[ref2]: http://www.regretsy.com
[some_name]: http://www.macdystopia.com

and will be stored in the variable "self.markers"

Now that we have all of th references, it's time to tell Sublime Text to show the list to us and let us choose one. We use the "show_quick_panel" function which is pretty easy to work with.

The "show_quick_panel" function returns the index of the value chosen by the user, which is passed into the method call "insert_link" which splits the string to get the reference title (i.e. "[ref1]") and then inserts it at the current position.

One last note. The class declaration is:

class ListLinksCommand(sublime_plugin.TextCommand):

To call this command from Sublime Text, the command name is "list_links". The camel case is converted to an underscore and "Command" is dropped from the name. So the keybinding looks like this:

{ "keys": ["super+alt+ctrl+l"], "command": "list_links" }

To use this plugin, I just created a new folder in the "Application Support/Sublime Text 2/Packages/" directory and dropped the Python script file in.

Conclusion

To figure this out, required that I look at a lot of other plugins. The Sublime Text API documentation is probably sufficient for someone that already has basic knowledge of making ST plugins. I found it a bit frustrating, but it was still easier than writing an AppleScript based function for BBEdit. I also like the functionality of the Sublime Text quick panel. Typing the name of a reference will automatically select it and narrow the list. It's fast.

I might build this into a plugin package but for right now I'm just putting together (and testing) some tools that fit my exact needs. I'm also use this to learn more about Sublime Text.

In the meantime, check out Brett's awesome MarkdownEditing package


  1. I do not like numbered link references. I have always used somewhat descriptive names instead. 

  2. You will also likely be offended by my awful code.  

blog comments powered by Disqus