Atom: How to create an Autocomplete Provider

This post will continue on Atom: How to complete an Autocomplete Provider

Hi all, today I’d like to see with you how to create a basic provider for Atom using the Autocomplete Provider API

In this specific case I‘d like to create a Provider for autocompletion of FactoryGirl.

First of all we need to generate a new package and we can do this by simply pressing cmd+shit+P and typing generate package then choosing Package Generator: Generate Package.

image

At this point Atom asks where to save the package we want to create, by default it saves the new packages in ~/github/package-name. To keep it simple we use the default base location so we also have the package loaded automatically.

The package generator creates a file structure that is more than what we need at the moment. For now we can keep only these files and directory:

lib/
.gitignore
CHANGELOG.md
LICENSE.md
package.json
README.md

Yes, without the spec dir, at least for now 😛

The main files we need in order to achieve our goal are lib/main.coffee and lib/provider.coffee. Let’s see how they have to be.

main.coffee

It’s the simplest one, it creates the package and defines the function getProvider

provider = require ‘./provider’

module.exports =
  activate: -> provider.load()

  getProvider: -> provider

provider.coffee

Now the “big” one, this file defines the provider used by the Autocomplete. In this class we need to define the getSuggestions function, clearly to return our suggestions 😀

fs = require ‘fs’

module.exports =
  selector: ‘.source.ruby’
  disableForSelector: ‘source.ruby .comment’

  load: () ->
    @completions = @scanFactories()

  getSuggestions: (request) ->
    {prefix} = request
    completions = []
    for factory in @completions when not prefix or firstCharsEqual(factory, prefix)
      completions.push(@buildCompletion(factory))
    completions

  scanFactories: () ->
    results = []
    factoryPattern = /factory :(w+)/g
    for factory_file in fs.readdirSync(“#{@rootDirectory()}/spec/factories”)
      data = fs.readFileSync “#{@rootDirectory()}/spec/factories/#{factory_file}”, ‘utf8’
      while (matches = factoryPattern.exec(data)) != null
        results.push matches[1]
    results

  rootDirectory: ->
    atom.project.rootDirectories[0].path;

  buildCompletion: (factory) ->
    text: “:#{factory}”
    rightLabel: ‘FactoryGirl’

firstCharsEqual = (str1, str2) ->
  str1[0].toLowerCase() is str2[0].toLowerCase()

By the way, the core function for our provider is scanFactories. This function watches in every file under {project_dir}/spec/factories/* and searches for the regular expression defined in factoryPattern. For example in a file like the following one scanFactories found [“user”, “confirmed_user”]

FactoryGirl.define do
  factory :user do
    username ‘foo’
        confirmed false

        factory :confirmed_user do
            confirmed true
        end
    end
end

With this provider we can get a result like this:

image

To improve a little bit the performance of this provider we filter the suggestions with firstCharsEqual, in this way we display only the factories that start with the same letter of the prefix, the ‘a’ of admin in the image above.

package.json

Last but not least, we need the package.json file, with this we can define the license, repo, author etc..

{
  “name”: “package_name”,
  “main”: “./lib/main”,
  “version”: “0.0.2”,
  “description”: “An autocomplete+ provider for FactoryGirl factories”,
  “keywords”: [
  ],
  “repository”: “https://github.com/package_repo”,
  “license”: “MIT”,
  “engines”: {
    “atom”: “>=1.0.0 <2.0.0”
  },
  “providedServices”: {
    “autocomplete.provider”: {
      “versions”: {
        “2.0.0”: “getProvider”
      }
    }
  },
  “dependencies”: {
  }
}

But the most important part for us is:

    “providedServices”: {
    “autocomplete.provider”: {
      “versions”: {
        “2.0.0”: “getProvider”
      }
    }
  },

Thanks to these lines we are telling Autocomplete how to get our new provider.

For now it’s all, now I’m fighting with a property available on the providers filterSuggestions that seem not to work as expected.

This is still a work in progress and not yet released as Atom package but it will be ASAP.

Stay tuned,
Bye!

Edit: Read the second part here

Leave a Reply

wpDiscuz