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
- Getting Started
- Plugin Overview
Handling Editor Events
Sending File to wakatime-cli
A WakaTime plugin is simple… just run the wakatime-cli command and pass it the current file as an argument.
$ wakatime-cli --entity myfile.txt
The code above sends a heartbeat to the WakaTime API and starts logging time.
Note: You might need to add
WakaTime plugins have two parts:
- editor plugin - the core which uses the editor's api and sends the currently focused file to wakatime-cli when the user moves the cursor or types some characters
- wakatime-cli - the common command line program which takes a file as input, detects the language and other metadata, then sends that data (aka "heartbeat") to the WakaTime api
Source code for existing plugins can be viewed on the WakaTime GitHub profile.
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, or download into
~/.wakatime/ if missing or needs an update
- Check for api key in
~/.wakatime.cfg, 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
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
lastHeartbeat variable with current file and current time
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:
Installing the wakatime-cli dependency
wakatime-cli is a Go command line tool containing common code that every plugin uses. It does things like detect the current project, 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.
The wakatime-cli binary is downloaded from GitHub releases. We also use the GitHub API to check the latest release version, and update wakatime-cli to the latest release if needed.
In your plugin’s init function, check if
~/.wakatime/wakatime-cli-- exists and that it’s version matches the latest GitHub release.
wakatime-cli needs updating or doesn’t exist, download the wakatime-cli zip file for the current OS and architecture from the latest release then unzip it into
Examples of downloading and unzipping wakatime-cli can be found here:
Listening for 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
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.
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.
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.
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(
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():
# do nothing
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
120 seconds is greater than the current time.
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.
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 [flags]
--alternate-language string Optional alternate language name. Auto-detected language takes priority.
--alternate-project string Optional alternate project name. Auto-detected project takes priority.
--api-url string API base url used when sending heartbeats and fetching code stats. Defaults to https://api.wakatime.com/api/v1/.
--category string Category of this heartbeat activity. Can be "coding", "building", "indexing", "debugging", "running tests", "writing tests", "manual testing", "code reviewing", "browsing", or "designing". Defaults to "coding".
--config string Optional config file. Defaults to '~/.wakatime.cfg'.
--config-read string Prints value for the given config key, then exits.
--config-section string Optional config section when reading or writing a config key. Defaults to [settings]. (default "settings")
--config-write stringToString Writes value to a config key, then exits. Expects two arguments, key and value. (default )
--cursorpos int Optional cursor position in the current file.
--disable-offline Disables offline time logging instead of queuing logged time.
--entity string Absolute path to file for the heartbeat. Can also be a url, domain or app when --entity-type is not file.
--entity-type string Entity type for this heartbeat. Can be "file", "domain" or "app". Defaults to "file".
--exclude strings Filename patterns to exclude from logging. POSIX regex syntax. Can be used more than once.
--exclude-unknown-project When set, any activity where the project cannot be detected will be ignored.
--extra-heartbeats Reads extra heartbeats from STDIN as a JSON array until EOF.
--help help for wakatime-cli
--hide-branch-names string Obfuscate branch names. Will not send revision control branch names to api.
--hide-file-names string Obfuscate filenames. Will not send file names to api.
--hide-project-names string Obfuscate project names. When a project folder is detected instead of using the folder name as the project, a .wakatime-project file is created with a random project name.
--hostname string Optional name of local machine. Defaults to local machine name read from system.
--include strings 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.
--include-only-with-project-file Disables tracking folders unless they contain a .wakatime-project file. Defaults to false.
--internal-config string Optional internal config file. Defaults to '~/.wakatime-internal.cfg'.
--key string Your wakatime api key; uses api_key from ~/.wakatime.cfg by default.
--language string Optional language name. If valid, takes priority over auto-detected language.
--lineno int Optional line number. This is the current line being edited.
--lines-in-file int Optional lines in the file. Normally, this is detected automatically but can be provided manually for performance, accuracy, or when using --local-file.
--local-file string Absolute path to local file for the heartbeat. When --entity is a remote file, this local file will be used for stats and just the value of --entity is sent with the heartbeat.
--log-file string Optional log file. Defaults to '~/.wakatime.log'.
--log-to-stdout If enabled, logs will go to stdout. Will overwrite logfile configs.
--no-ssl-verify Disables SSL certificate verification for HTTPS requests. By default, SSL certificates are verified.
--offline-count Prints the number of heartbeats in the offline db, then exits.
--plugin string Optional text editor plugin name and version for User-Agent header.
--project string Override auto-detected project. Use --alternate-project to supply a fallback project if one can't be auto-detected.
--proxy string Optional proxy configuration. Supports HTTPS SOCKS and NTLM proxies. For example: 'https://user:pass@host:port' or 'socks5://user:pass@host:port' or 'domain\user:pass'
--ssl-certs-file string Override the bundled CA certs file. By default, uses system ca certs.
--sync-offline-activity string Amount of offline activity to sync from your local ~/.wakatime.bdb bolt file to your WakaTime Dashboard before exiting. Can be "none" or a positive integer. Defaults to 1000, meaning after sending a heartbeat while online, all queued offline heartbeats are sent to WakaTime API, up to a limit of 1000. Can be used without --entity to only sync offline activity without generating new heartbeats. (default "1000")
--time float Optional floating-point unix epoch timestamp. Uses current time by default.
--timeout int Number of seconds to wait when sending heartbeats to api. Defaults to 120 seconds. (default 120)
--today Prints dashboard time for Today, then exits.
--today-goal string Prints time for the given goal id Today, then exits Visit wakatime.com/api/v1/users/current/goals to find your goal id.
--today-hide-categories When optionally included with --today, causes output to show total code time today without categories.
--verbose Turns on debug messages in log file.
--version Prints the wakatime-cli version number, then exits.
--write When set, tells api this heartbeat was triggered from writing to a file.
Some examples of executing
Tracking Last Heartbeat
After sending the currently focused file to
wakatime-cli, your plugin should update the
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.
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
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_at timestamp update every time a heartbeat is received.
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
wakatime-cli will write DEBUG messages to the
Things to do once your plugin is sending heartbeats.
Keep track of your plugin's version number with Semantic Versioning.
Send your plugin's version number and name to
wakatime-cli with the
Put it on GitHub and send us your repository in an email to firstname.lastname@example.org.
Each editor/IDE does this differently. Some editors come with built-in plugin management, where you submit a new version and it handles updating.