Kite augments your coding environment with all the internet’s programming knowledge. We've made plugins for various editors so that you can start using Kite regardless of the editor you use. If you don't see a plugin for your favorite editor, feel to request one, or, better yet, go ahead and write one yourself! Checkout the Contributing section below.
When you download and install Kite, plugins for various editors will be installed by Kite automatically.
Please note that these plugins do not provide the full Kite experience. These are the plugins that allow your editor to work with Kite. You will still need to download and install the Kite application. To sign up for an invite, you can click here.
Kite currently supports vim
, Sublime Text
, PyCharm
and Atom
, and we expect the list to grow fast! In the spirit of bring-your-own-editor, we have made it extremely easy to create editor plugins for Kite. Here we provide guidelines for developing your own. Help us bring Kite to many more editors! Check out CONTRIBUTING to read more about how to contribute to Kite plugins.
The plugin needs to do two things:
- Sending editor events to Kite.
- Receiving diffs and applying them to the editor.
We will now go through how to accomplish these two tasks in more detail.
To support sending events to Kite:
- Be able to grab:
- Current buffer contents
- Full filename
- Current cursor position
- Any selections (if they exist)
- Be able to distinguish between an "edit" or "selection" event:
- An "edit" event is a change in buffer contents. It need not be a single character change.
- A selection event is when the cursor is moved, or selection is changed without content being edited. Note: Sometimes editors will trigger an edit AND selection for regular typing (b/c the buffer was modified AND the cursor moved as a result of entering text). It's OK to send both of these events,
libkited
will correctly dedupe and send what is needed. - The cursor position is represented as a selection with
start == end
(see below). - These events should only be sent for the currently active buffer. Changes made to a non-active file (e.g. through multi-file find/replace) should not be sent.
- The ability to write json blobs to a unix domain socket (or UDP, more details to come):
libkited
listens to json objects sent to$HOME/.kite/kite.sock
- A typical example of json structure being sent by vim is:
{
"source": "vim",
"filename" : "/full/path/to/file/with/extension.ext",
"action": "edit", # could be "selection",
"text": <buffer contents>,
"selections": [{"start": 5, "end": 5}, {"start": 10, "end": 20}...],
}
Note: the source
field should identify the particular editor. It's okay if multiple processes are open that use the same source
. For PyCharm (which is a fork of IntelliJ targeting Python programmers) we still use "intellij" because it's the same code base as other IntelliJ forks or IntelliJ itself.
Note 2: filename
s should be sent after resolving symlinks, .
, and ..
, and getting to the underlying canonical file path. In Java you can do this with getCanonicalPath()
. In Python you can do this with os.realpath
. filename
is necessary so kite can make sure it's in a user-authorized directory.
Note 3: before sending an event, check if the text
is longer than 2^20 (1024*1024) characters. If it is, replace the event's action with skip
and its contents with "file_too_large".
Here's a quick example of how this looks in python - it may vary based on the language you are working on for the plugin.
SOCK_PATH = os.path.expandvars("$HOME/.kite/kite.sock")
uds = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
uds.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2<<20) # 2mb socket buffer
uds.sendto(json_body, SOCK_PATH)
To receive diffs, you need to create and listen on a new unix domain socket:
- This unix domain socket must live in
$HOME/.kite/plugin_socks/
. - If your editor manages instances (e.g Sublime Text, when there's only ever one instance), you can use a socket name such as "sublime3". If your editor can have multiple instances (e.g
vim
oremacs
), then you need to create a unique name. Forvim
, we simply use auuid
generator, and create sockets with the namevim-<uuid>
. - For any outgoing events you send on
$HOME/.kite/kite.sock
, you must add a new field:pluginId
, set to the name of the unix domain socket you created. - Be sure to remove this socket on editor close.
How this works: If a diff is generated by an event, the backend will copy the pluginId
back into the response sent back to libkited
which sends it along to the UI. If the user clicks on "apply", the sidebar contacts an endpoint at /clientapi/python/applydiff
hosted by libkited
, sending along the contents of the diff suggestion object. libkited
is able to find your unix domain socket in $HOME/.kite/plugin_socks
by name (via pluginId
) and send the diff suggestion object to it. The diff suggestion object has the format:
{
"type": # either apply, highlight, clear>,
"score": # backend score, unused,
"plugin_id": # plugin_id,
"file_md5": # md5 of file as it was when this suggestion was computed (lowercase hex string),
"file_base64": # base64 encoded version of file when suggestion was computed,
"diffs": <array of diff objects>,
}
The file_md5
and file_base64
fields assume a utf-8 encoding of the file contents.
Diff objects:
{
"type": "missing_import" or "typo",
"linenum": # line number of the change,
"begin": # byte offset to start of change,
"end": # byte offset to end of change,
"source": # what content looks like now,
"destination": # what the content should look like,
"line_src": # line number of the original contents
"line_dst": # line number of the new contents,
}
Application of these diffs, and keeping track of offset changes as you apply a series of diff objects that may be part of the suggestion is up to the plugin implementation.
After each apply, editor plugins should immediately execute an automatic "clear".
A "clear" event means: clear every highlight that the plugin has added in the past. You should probably therefore not use the diffs
supplied with this event. Strictly speaking there could have been more things highlighted than are in the diffs
field.
Each plugin source file should begin with the following comment:
# Contents of this plugin will be reset by Kite on start. Changes you make
# are not guaranteed to persist.
All offsets and lengths related to strings are specified in units of characters (i.e., unicode code points), not bytes (relevant for handling unicode strings).
Events with an action called focus
should be sent every time the user changes the currently-focused file in the editor, AND every time the editor window gains OS X focus.
Events with an action called lost_focus
should be sent every time the editor window loses OS X focus.
Raising an event when the window gains OS X focus can be impossible with terminal editors. For example, this discussion seems to indicate decisively that there is no reliable vim hook for this. The draft plan is currently to use the accessibility API to track when terminal tabs get focus, and use a heuristic to match them to any running vim editor (still a todo).
Some editors synchronize multiple buffers if they're for the same file, even without saves, e.g. IntelliJ, Eclipse, while other editors do not, e.g. Sublime Text.
The big saving grace here are the focus
events. Without them and their repeat of the buffer contents, we'd have more trouble with this corner case.
vim:
Tarak Upadhyaya, Alex FlintSublime Text:
Tarak Upadhyaya, Alex FlintPyCharm:
Adam SmithAtom:
Tarak Upadhyaya, Alex Flint
All the software in this repository is released under the MIT License. See LICENSE for details.