Creating a Plugin

This guide will help you create a new WakaTime plugin for your text editor/IDE.

Please let us know if you're building a WakaTime plugin! We can help you through Slack or email!

We can also evangelize and help promote your new plugin to all WakaTime users.

Table of Contents

  1. Getting Started
  2. Plugin Overview
  3. Plugin Initialization
  4. Handling Editor Events
  5. Sending File to wakatime-cli
  6. Debugging
  7. Releasing

Getting Started

A WakaTime plugin is simple… just run the wakatime-cli command and pass it the current file as an argument.

$ pip install wakatime $ wakatime --file myfile.txt

The code above sends a heartbeat to the WakaTime API and starts logging time.

Note: You might need to add --apikey XXXX.

WakaTime plugins have two parts:

  1. editor plugin : the core piece which uses the editor's api and passes the current file to wakatime-cli
  2. wakatime-cli : the common python package which sends heartbeats to the WakaTime api

Source code for existing plugins can be viewed on the WakaTime GitHub profile.

Plugin Overview

This is a high-level overview of a WakaTime plugin from the time it's loaded, until the editor is exited.

  • Plugin loaded by text editor/IDE, runs plugin's initialization code
  • initialization code
    • Setup any global variables, like plugin version, editor/IDE version
    • Check for wakatime-cli, download into plugin directory if does not exist
    • Check for python, download and install if does not exist (Windows only)
    • Check for api key, prompt user to enter if does not exist
    • Setup event listeners to detect when current file changes, a file is modified, and a file is saved
  • Current file changed (our file change event listener code is run)
    • go to Send heartbeat function with isWrite false
  • User types in a file (our file modified event listener code is run)
    • go to Send heartbeat function with isWrite false
  • A file is saved (our file save event listener code is run)
    • go to Send heartbeat function with isWrite true
  • Send heartbeat function
    • check lastHeartbeat variable. if isWrite is false, and file has not changed since last heartbeat, and less than 2 minutes since last heartbeat, then return and do nothing
    • run wakatime-cli in background process passing it the current file
    • update lastHeartbeat variable with current file and current time

Plugin Initialization

This happens every time your plugin is loaded, for example when the user opens their text editor/IDE or when the user first installs the plugin.

Examples of init code can be viewed here:

Checking for wakatime-cli

wakatime-cli is a Python module containing common code that every plugin uses. It does things like detect the current project, current branch, syntax language, and send heartbeats to the WakaTime api. To send a heartbeat, your plugin should run wakatime-cli in a background process and pass it the absolute path of the current file.

Some plugins are able to include wakatime-cli along with the plugin code, or using a build script. If your text editor/IDE can not include resource files in plugin distributions, in the plugin's init function check that the wakatime/cli.py file exists in the plugin's local directory. If the wakatime/cli.py file is not found, download wakatime-cli from the GitHub zip archive, then unzip it into the plugin's local directory.

Examples of downloading and unzipping wakatime-cli can be found here:

Checking for Python

Since wakatime-cli is a Python module, in your plugin's init function you should make sure Python is installed on the user's machine. If python isn't found (usually only on Windows machines) then your plugin should download and install Python.

Examples of detecting Python are here:

Examples of downloading and installing Python can be viewed here:

Binding to Editor Events

The last step when initializing your plugin is setting up event listeners. Most editors support some version of these events:

  • currently focused file has changed (for example, switching from file A to file B)
  • current file was modified
  • a file was saved

These three events are the only ones we care about. When one of these three events are detected, your plugin should send the file to wakatime-cli.

Some examples of listening for these events:

Handling Editor Events

Every event handler should call the same function, sometimes passing the currently focused file as a parameter if needed. The only catch is the File Save event needs to pass a boolean to indicate this heartbeat was triggered from saving to a file.

File Changed

The file changed event detects when the editor focus has changed to a new file. For example, when the user switches to a new tab in their editor.

File Modified

The file modified event should be triggered every time the currently focused file is changed. If your editor only supports detecting the first time a file is modified, you should detect when a user presses a key instead.

File Saved

The file saved event detects when a file's modified contents are written to disk.

Sending File to wakatime-cli

Every event handler should call the same function. This common function should accept one argument, the boolean indicating whether this event was triggered from saving to a file(true) or not(false).

The first thing this function should do is detect the currently focused file. If there is no way to detect the currently focused file, the event handlers can pass the current file as an argument.

Deciding to Send File or Not

This function should decide whether to send the file to wakatime-cli or not, depending on how long it has been since it last sent the same file to wakatime-cli. Here's some example Python showing this decision:

if enoughTimeHasPassed(lastSentTime) or currentlyFocusedFileHasChanged(lastSentFile) or isFileSavedEvent(): sendFileToWakatimeCLI() else: # do nothing pass

The enoughTimeHasPassed function checks if more than 2 minutes have passed since this function has executed last, to prevent sending the currently focused file to wakatime-cli too frequently. In this case, it would check if lastSentTime times 120 seconds is greater than the current time.

The currentlyFocusedFileHasChanged function checks if the absolute path to the currently focused file equals lastSentFile and returns True if it does not equal and False if it is equal.

The isFileSavedEvent function checks the boolean passed from the event handlers, and returns it as-is.

Executing Background Process

Now that we have decided to send the file to wakatime-cli, we need to execute it as a background process and pass the current file as a command line argument. For reference, here is the wakatime-cli help:

$ python ./wakatime/cli.py --help usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN] [--time time] [--lineno LINENO] [--cursorpos CURSORPOS] [--entity-type ENTITY_TYPE] [--proxy PROXY] [--project PROJECT] [--alternate-project ALTERNATE_PROJECT] [--hostname HOSTNAME] [--disableoffline] [--hidefilenames] [--exclude EXCLUDE] [--include INCLUDE] [--logfile LOGFILE] [--apiurl API_URL] [--timeout TIMEOUT] [--config CONFIG] [--verbose] [--version] Common interface for the WakaTime api. optional arguments: -h, --help show this help message and exit --entity FILE absolute path to file for the heartbeat; can also be a url, domain, or app when --entity-type is not file --key KEY your wakatime api key; uses api_key from ~/.wakatime.conf by default --write when set, tells api this heartbeat was triggered from writing to a file --plugin PLUGIN optional text editor plugin name and version for User- Agent header --time time optional floating-point unix epoch timestamp; uses current time by default --lineno LINENO optional line number; current line being edited --cursorpos CURSORPOS optional cursor position in the current file --entity-type ENTITY_TYPE entity type for this heartbeat. can be one of "file", "domain", or "app"; defaults to file. --proxy PROXY optional https proxy url; for example: https://user:pass@localhost:8080 --project PROJECT optional project name --alternate-project ALTERNATE_PROJECT optional alternate project name; auto-discovered project takes priority --hostname HOSTNAME hostname of current machine. --disableoffline disables offline time logging instead of queuing logged time --hidefilenames obfuscate file names; will not send file names to api --exclude EXCLUDE filename patterns to exclude from logging; POSIX regex syntax; can be used more than once --include INCLUDE filename patterns to log; when used in combination with --exclude, files matching include will still be logged; POSIX regex syntax; can be used more than once --logfile LOGFILE defaults to ~/.wakatime.log --apiurl API_URL heartbeats api url; for debugging with a local server --timeout TIMEOUT number of seconds to wait when sending heartbeats to api --config CONFIG defaults to ~/.wakatime.conf --verbose turns on debug messages in log file --version show program's version number and exit

Some examples of executing wakatime-cli:

Tracking Last Heartbeat

After sending the currently focused file to wakatime-cli, your plugin should update the lastSentTime and lastSentFile global variables with the current time and currently focused file. This way they will be ready the next time we are handling an event.

Debugging

Some resources you can use while debugging your plugin.

Confirming Heartbeat Received

To confirm your heartbeats are being received by the WakaTime API, check the current user resource and look for changes to the last_plugin and last_heartbeat attributes:

https://wakatime.com/api/v1/users/current

If your plugin is working, you should see the last_plugin attribute showing your plugin's name and version. You will also see the last_heartbeat timestamp update every time a heartbeat is received.

Logging

Logging errors and info messages from your plugin is a good way to debug problems. Each editor/IDE running your plugin usually provides a log for appending messages. You should setup logging to write there from your plugin and watch it while debugging.

wakatime-cli has it's own log file located at $HOME/.wakatime.log. To turn on verbose logging, pass the --verbose argument to wakatime-cli. With the --verbose flag, wakatime-cli will write DEBUG messages to the $HOME/.wakatime.log file.

Releasing

Things to do once your plugin is sending heartbeats.

Versioning

Keep track of your plugin's version number with Semantic Versioning.

Send your plugin's version number and name to wakatime-cli with the --plugin argument.

Distribution

Put it on GitHub and send us your repository in an email to alan@wakatime.com.

Auto Updating

Each editor/IDE does this differently. Some editors come with built-in plugin management, where you submit a new version and it handles updating.