Hacking the hackable editor
Creating your first Atom plugin
What is this tutorial for?
Today, we will be creating our first atom plugin. We will be creating a plugin that implements the basic functionality of a dictionary lookup. It will allow a user to search a word directly in the editor, by either highlighting the word or placing the cursor somewhere in the word. You can follow this post along with the YouTube video here.
What is Atom?
Atom is a free and open-source source code editor developed by GitHub. The editor is meant to be customized in every possible way. It is built on a Chromium browser with HTML, Javascript, CSS, and Node.js and has complete access to the standard developer console window in Chrome.
Creating an Atom Plugin
To get started building your first Atom Plugin, you must have Atom installed. If you do not already have it, go grab it for free from their website: https://atom.io/
So, let’s get started with our first plugin!
Package Structure
Atom stores all it’s plugins within the atom folder, .atom
. It is located at \Users\user-name\.atom
. Within the folder, there is a subfolder called packages
. Which, as you may have guessed, is where all the packages can be found. Every package has its own folder.
The first step is to create a package for the plugin. I will create a folder called kims-dictionary-plugin
.
Depending on the type of package you want to build, you may not require all these folders (or you may require additional ones). Nonetheless, this gives you a good breakdown of what a basic package structure looks in Atom:
my-first-plugin/
├─ keymaps/
├─ lib/
├─ menus/
├─ spec/
├─ styles/
└─ package.json
Now let’s take a look at some of these folders and files.
package.json
For our package to be recognized by Atom, we need to add a .json file: package.json
. This json file will contain metadata about the package and is based off of the regular Node package.json
keys (https://docs.npmjs.com/files/package.json).
A general structure and important keys to implement are as follows:
Keys explained..
name
is the name of the packagemain
is the location of entry point to the plugin, kind of like main (usually located in the lib folder named as plugin-name.coffee.. and if themain
key is not in yourpackage.json
file, Atom will default to looking for anindex.coffee
orindex.js
file)version
is the version number of the package, and must follow the convention:major.minor.bug
(you should indicate0.0.0
for now)description
informs others about what the package doesengines
indicates the minimal required version of Atomdependencies
indicates other packages neededrepository
is a URL indicating where the public repository is locatedbugs
is a URL where others can report issueslicense
indicates the license
Some other keys…
styles
: an Array of Strings describing the order in which your style sheets need to be loaded (if not specified they will be loaded alphabetically)keymaps
: an Array of Strings i describing the order in which your key mappings need to be loaded (if not specified they will be loaded alphabetically)menus
: an Array of Strings describing the order in which your menu mappings eed to be loaded (if not specified they will be loaded alphabetically)
There are many other keys, however for now this is as much as we need to create our plugin.
Now that your package has a valid package.json
file, Atom can recognize it and load it. However, it’s totally useless right now. So, it’s time to make it useful by giving it some features!
Note: Code snippets will be written mainly in EcmaScript standard for JavaScript.
Package logic
Our package logic can be found at plugin-name.coffee/.js.
- your-plugin.coffee/your-plugin.js handles the logic of the plugin and can be viewed as the main package file
- your-plugin-view.coffee/.js handles the UI elements of the package
In your main, you usually have the following 3 methods: activate
, deactivate
, serialize
.
- The
deactivate
method destroys the various class instances created - The
serialize
method passes on the serialization to the View class - The
activate
method is what is executed when your plugin is loaded and instantiates the view class.
Here is an example skeleton for my-first-plugin.js:
Code explained: Commands are events triggered by a user and packages can subscribe to commands. By subscribing, they can execute code in response to the event (a.k.a the command) that was triggered by the user. When we want to subscribe to multiple events that’s where the CompositeDisposable class comes in handy, but we won’t get into any specifics right now.
The toggle
method is what gets called every time the user wants to run the package (‘toggles the package’). It is invoked either by the menu item or a hotkey.
Here is an example skeleton for my-first-plugin-view.js:
Adding Code
So let’s dive in. First, we want to grab the word we want to lookup.
We do this by creating an reference to the current state of the editor and finding the word that is either selected or where the user’s cursor is. We will add this snippet to our toggle method. Also, don’t forget to define the variable wordToSearch:
wordToSearch: null,
Next, we will add a method in our view to update itself.
In our view, the root element only had one child (index starts at 0) and we want to update it’s text to reflect the word we just grabbed from the editor. Afterwards, we will need to go back into our logic and add the following line:
this.MyFirstPluginView.setElement();
With this line, we will call the setElement method we created which will update the element with our word.
At this point, you may notice that as you make changes to your package the changes aren’t reflected right away. You need to reload Atom, either by closing and re-opening or hitting View > Developer > Reload. This will ensure Atom runs the latest version of our source code.
Therefore, reload Atom and toggle the plugin. The panel now displays the word where the cursor is focused or highlighted.
Let’s take a moment to look at the flow of our package in Atom:
- First, Atom starts up.
- Second, Atom begins loading packages.
- When Atom reaches our plugin it reads the
package.json
- Atom is going to load in our keymaps, menus, styles and main
- At some point in time while the user is using Atom, uur package gets toggled
your-plugin:toggle
- Once our plugin is toggled, Atom executes the
activate
method. The activate method sets up the UI by creating a hidden view and then executesyour-pluggin:toggle
which reveals the hidden view! - Lastly, when toggle is called again Atom executes the toggle command again which hides the view.
Connecting to an API
Now, we will need to connect our plugin with some sort of Dictionary API. For this tutorial I have chosen the Oxford Dictionaries, but feel free to use any Dictionary API that your comfortable with.
This may not be the most efficient way to put through a request to the API and process the results, but here is how I did it:
I send an XMLHttpRequest to the API by providing my credentials and adding an event listener to the request. This event listener, when it receives a notification that transactions has been completed, it will parse the results (which is in JSON format). The results I get back contain a ton of information, however all we need for the sake of this tutorial is the first definition returned.
For now, we will place this logic in our toggle method. Don’t forget to define our variable that stores the definition:
wordDefinition: null,
We will need to update our view to reflect the new changes we made.
Reload atom and toggle again… Volià!
You did it. Your first plugin, check!
I went a head and added some CSS under the styles folder.
You can find the link to my repository here: https://github.com/kim-codes/kims-dictionary-plugin
Take it for a test drive, add some functionality, improve on it.. but most of all enjoy :)