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
.

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:

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