Sublime Text 3 – creating a plugin

February 18, 2019
plugin ST3 dev npaste

I’ve written npaste, a simple pastebin for text and images that supports encryption using GPG. After a while of using it via the command line I felt the need for a plugin for Sublime Text 3 and also Visual Studio Code. In this post I will explain how I created the plugin for Sublime Text 3.

Resources

The ST3 plugin API is pretty well-documented, and there are also some resources available on the Package Control website that explains how to create a plugin. Despite this I had to do some research on my own, and the point of this post is to document the not so well documented aspects of creating and publishing an ST3 plugin.

Create the plugin

The first thing I did was to create a default plugin file. You can easily do this from within Sublime Text 3 by choosing Tools > Developer > New Plugin... from the Main Menu.

The plugin file will look something along the lines of this:

import sublime
import sublime_plugin


class ExampleCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        self.view.insert(edit, 0, "Hello, World!")

ST3 treats these classes as commands without the Command part and in snake case. This ExampleCommand would be executed by telling ST3 to run the command example.

In my case, I named the class NpasteUploadCommand which can be executed by calling npaste_upload. More on that later.

The actual actions that the command will execute should be placed in the run method, but I’m not going to cover that in this post.

Saving the file

I created an empty git repository at ~/dev/npaste-sublime and saved the file as npaste-upload.py inside of this folder.

To be able to test the plugin as I developed it, I created a symbolic link inside of the Packages folder of ST3. Where this folder is located probably differs depending on which OS you’re using, but in my case, as I’m using a Mac, it was located at ~/Library/Application Support/Sublime Text 3/Packages. I basically did this:

ln -s ~/dev/npaste-sublime ~/Library/Application\ Support/Sublime\ Text\ 3/Packages/npaste

Now Sublime Text 3 will load this plugin and automatically reload it whenever I make changes to any files within that folder.

Keybindings

ST3 allows you to easily configure default keybinding for each operating system it supports (Windows, Mac, Linux). To do this, simply create a file for each OS you want to support inside the package folder. The valid file names are:

  • Default (Windows).sublime-keymap
  • Default (OSX).sublime-keymap
  • Default (Linux).sublime-keymap

Inside of each file you can define keybindings by using the following syntax:

[
  {
    "keys": ["ctrl+alt+shift+n"],
    "command": "npaste_upload"
  }
]

As you can see, the command that will be executed when you type ctrl+alt+shift+n is the previously mentioned npaste_upload command.

Dependencies

My plugin depends on the requests package. To make sure that this package will be installed when installing the npaste plugin, I created a file in the root of the package folder called dependencies.json. The format of this file is documented in the Package Control Documentation, but in my case it is the following:

{
  "*": {
    "*": [
      "requests"
    ]
  }
}

Context Menu

I wanted to have a menu entry when right clicking in a buffer, so I added a Context Menu entry. To do this, I created a folder called menus in the root of my package folder. Inside of this folder I placed a file called Context.sublime-menu with the following contents:

[
  {
    "id" : "npasteUpload",
    "caption" : "Paste to npaste",
    "command" : "npaste_upload"
  }
]

To be able to easily edit the settings of the npaste plugin, I wanted to add a menu entry to Sublime Text > Preferences > Package Settings > npaste called Settings. To do so, I created a file inside of the menus folder called Main.sublime-menu. The contents of this file is as following:

[
  {
    "id": "preferences",
    "children": [
      {
        "id": "package-settings",
        "children": [
          {
            "caption": "npaste",
            "children": [
              {
                "caption": "Settings",
                "command": "edit_settings",
                "args": {
                  "base_file": "${packages}/npaste/settings/npaste.sublime-settings"
                }
              }
            ]
          }
        ]
      }
    ]
  }
]

Settings

As you can see, the Main Menu entry points to a file called settings/npaste.sublime-settings. This file contains the default settings of the plugin. Since npaste relies on a remote server, the user must be able to set the hostname, username and password, along with some other settings. I created a folder in the root of the package folder called settings. Inside of this folder I created a file called npaste.sublime-settings with the following contents:

{
//  Specifies the URL of the npaste server to use
    "npaste.url": "https://localhost:3000/",

//  Specifies the npaste API username
    "npaste.username": "username",

//  Specifies the npaste API password
    "npaste.password": "password",

//  If true, pastes will be archived instead of deleted when they expire.
    "npaste.archive": true,

//  Sets the age of pastes (s,m,h,d,y)
    "npaste.age": "14d",

//  If true, pastes will be encrypted using GPG
    "npaste.encrypt": true,

//  Encryption key length
    "npaste.encryption_key_length": 32
}

Prepending all the keys with npaste. shouldn’t be necessary and it’s something I will improve in a later update.

Messages

You’ve probably seen that some packages displays a text file after you’ve installed it or after an upgrade. I wanted this for my npaste plugin, so I created a new folder called messages. Inside of this folder I created two files: install.txt and 0.0.5.txt. The contents of install.txt will be displayed when a user installs the package, and the contents of 0.0.5.txt will be displayed when a user upgrades the package to version 0.0.5. You can read more about this in the Package Control documentation.

Finally I created a file in the root of the package folder called messages.json. The contents of this file is the following:

{
  "install": "messages/install.txt",
  "0.0.5": "messages/0.0.5.txt"
}

LICENSE and README files

You should add a LICENSE file to the root folder of your package. The Package Control bot will give you a warning if you don’t have this file in your repository. It’s also advisable to add a README file of some kind.

Folder structure

This is the structure of my package folder after doing all of the above steps:

.
├── Default (Linux).sublime-keymap
├── Default (OSX).sublime-keymap
├── Default (Windows).sublime-keymap
├── LICENSE
├── README.md
├── dependencies.json
├── menus
│   ├── Context.sublime-menu
│   └── Main.sublime-menu
├── messages
│   ├── 0.0.5.txt
│   └── install.txt
├── messages.json
├── npaste-upload.py
└── settings
    └── npaste.sublime-settings

Publishing the package

This is well-documented in the Package Control Documentation, so I’m not going to cover it here. The only thing that puzzled me was that there was no description below the package name when searching for the package within Package Control, so I had to do some research on this. It turns out that the description is fetched from the description of your repository, so be sure to add a short description to your repository.

You can verify that this post was written by me by pasting the signature into keybase.io/verify.