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:

https://gist.github.com/kim-codes/ce8f510250876530d9e6b3070514884f

Keys explained..

  • name is the name of the package
  • main 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 the main key is not in your package.json file, Atom will default to looking for an index.coffee or index.js file)
  • version is the version number of the package, and must follow the convention: major.minor.bug (you should indicate 0.0.0 for now)
  • description informs others about what the package does
  • engines indicates the minimal required version of Atom
  • dependencies indicates other packages needed
  • repository is a URL indicating where the public repository is located
  • bugs is a URL where others can report issues
  • license 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:

https://gist.github.com/kim-codes/9b1d797a28ff69e5e40406d5ae41a3da

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:

https://gist.github.com/kim-codes/8e3864173c23b200257de401cb04758b

Adding Code

So let’s dive in. First, we want to grab the word we want to lookup.

https://gist.github.com/kim-codes/7c2e5e9674a2aab41d518a7ecd906173

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.

https://gist.github.com/kim-codes/51011b1b396163a901603e246eddcca9

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 executes your-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:

https://gist.github.com/kim-codes/3287c5b82e846676836298044c3ee2a1

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 :)