Replace snippet loader task w/ async loading

This commit is contained in:
Nathan Sobo
2013-03-14 16:46:08 -06:00
committed by Corey Johnson & Nathan Sobo
parent b01a3ff9cc
commit e5436974eb
7 changed files with 118 additions and 137 deletions

View File

@@ -11,7 +11,8 @@
"underscore": "1.4.4",
"d3": "3.0.8",
"coffee-cache": "0.1.0",
"pegjs": "0.7.0"
"pegjs": "0.7.0",
"async": "0.2.6"
},
"scripts": {

View File

@@ -1,56 +0,0 @@
fs = require 'fs-utils'
TextMatePackage = require 'text-mate-package'
SnippetBodyParser = require './snippet-body-parser'
CSON = require 'cson'
module.exports =
snippetsLoaded: (snippets) ->
for snippet in snippets
for selector, snippetsByName of snippet
for name, attributes of snippetsByName
attributes.bodyTree = SnippetBodyParser.parse(attributes.body)
callTaskMethod('snippetsLoaded', snippets)
loadTextMateSnippets: (path) ->
snippetsDirPath = fs.join(path, 'Snippets')
snippets = []
for snippetsPath in fs.list(snippetsDirPath)
logWarning = ->
console.warn "Error reading TextMate snippets file '#{snippetsPath}'"
continue if fs.base(snippetsPath).indexOf('.') is 0
try
if CSON.isObjectPath(snippetsPath) and object = CSON.readObject(snippetsPath)
snippets.push(object)
else if object = fs.readPlist(snippetsPath)
snippets.push(object)
else
logWarning()
catch e
logWarning()
@snippetsLoaded(@translateTextmateSnippets(snippets))
loadAtomSnippets: (path) ->
snippetsDirPath = fs.join(path, 'snippets')
snippets = []
for snippetsPath in fs.list(snippetsDirPath)
continue if fs.base(snippetsPath).indexOf('.') is 0
try
snippets.push(CSON.readObject(snippetsPath))
catch e
console.warn "Error reading snippets file '#{snippetsPath}'"
@snippetsLoaded(snippets)
translateTextmateSnippets: (tmSnippets) ->
atomSnippets = {}
for { scope, name, content, tabTrigger } in tmSnippets
if scope
scope = TextMatePackage.cssSelectorFromScopeSelector(scope)
else
scope = '*'
snippetsForScope = (atomSnippets[scope] ?= {})
snippetsForScope[name] = { prefix: tabTrigger, body: content }
[atomSnippets]

View File

@@ -1,34 +0,0 @@
Task = require 'task'
TextMatePackage = require 'text-mate-package'
module.exports =
class LoadSnippetsTask extends Task
constructor: (@snippets) ->
super('snippets/lib/load-snippets-handler')
@packages = atom.getLoadedPackages()
@packages.push(path: config.configDirPath)
started: ->
@loadNextPackageSnippets()
loadNextPackageSnippets: ->
unless @packages.length
@done()
@snippets.loaded = true
return
@packageBeingLoaded = @packages.shift()
if @packageBeingLoaded instanceof TextMatePackage
@loadTextMateSnippets(@packageBeingLoaded.path)
else
@loadAtomSnippets(@packageBeingLoaded.path)
loadAtomSnippets: (path) ->
@callWorkerMethod('loadAtomSnippets', path)
loadTextMateSnippets: (path) ->
@callWorkerMethod('loadTextMateSnippets', path)
snippetsLoaded: (snippets) ->
@snippets.add(snippet) for snippet in snippets
@loadNextPackageSnippets()

View File

@@ -1,10 +1,12 @@
AtomPackage = require 'atom-package'
fs = require 'fs-utils'
fs = require 'fs'
fsUtils = require 'fs-utils'
_ = require 'underscore'
SnippetExpansion = require './snippet-expansion'
Snippet = require './snippet'
LoadSnippetsTask = require './load-snippets-task'
TextMatePackage = require 'text-mate-package'
CSON = require 'cson'
async = require 'async'
module.exports =
snippetsByExtension: {}
@@ -20,19 +22,82 @@ module.exports =
@loadSnippetsTask?.abort()
loadAll: ->
@loadSnippetsTask = new LoadSnippetsTask(this)
@loadSnippetsTask.start()
packages = atom.getLoadedPackages()
packages.push(path: config.configDirPath)
async.eachSeries packages, @loadSnippetsFromPackage.bind(this), @doneLoading.bind(this)
loadDirectory: (snippetsDirPath) ->
for snippetsPath in fs.list(snippetsDirPath) when fs.base(snippetsPath).indexOf('.') isnt 0
snippets.loadFile(snippetsPath)
doneLoading: ->
@loaded = true
loadFile: (snippetsPath) ->
try
snippets = CSON.readObject(snippetsPath)
catch e
console.warn "Error reading snippets file '#{snippetsPath}'"
@add(snippets)
loadSnippetsFromPackage: (pack, done) ->
if pack instanceof TextMatePackage
@loadTextMateSnippets(pack.path, done)
else
@loadAtomSnippets(pack.path, done)
loadAtomSnippets: (path, done) ->
snippetsDirPath = fsUtils.join(path, 'snippets')
loadSnippetFile = (filename, done) =>
return done() if filename.indexOf('.') is 0
filepath = fsUtils.join(snippetsDirPath, filename)
CSON.readObjectAsync filepath, (err, object) =>
if err
console.warn "Error reading snippets file '#{filepath}': #{err.stack}"
else
@add(object)
done()
fs.readdir snippetsDirPath, (err, paths) ->
async.each(paths, loadSnippetFile, done)
loadTextMateSnippets: (path, done) ->
snippetsDirPath = fsUtils.join(path, 'Snippets')
loadSnippetFile = (filename, done) =>
return done() if filename.indexOf('.') is 0
filepath = fsUtils.join(snippetsDirPath, filename)
logError = (err) ->
console.warn "Error reading snippets file '#{filepath}': #{err.stack ? err}"
try
readObject =
if CSON.isObjectPath(filepath)
CSON.readObjectAsync.bind(CSON)
else
fsUtils.readPlistAsync.bind(fsUtils)
readObject filepath, (err, object) =>
try
if err
logError(err)
else
@add(@translateTextmateSnippet(object))
catch err
logError(err)
finally
done()
catch err
logError(err)
done()
return done() unless fsUtils.isDirectory(snippetsDirPath)
fs.readdir snippetsDirPath, (err, paths) ->
if err
console.warn err
return done()
async.each(paths, loadSnippetFile, done)
translateTextmateSnippet: ({ scope, name, content, tabTrigger }) ->
scope = TextMatePackage.cssSelectorFromScopeSelector(scope) if scope
scope ?= '*'
snippetsByScope = {}
snippetsByName = {}
snippetsByScope[scope] = snippetsByName
snippetsByName[name] = { prefix: tabTrigger, body: content }
snippetsByScope
add: (snippetsBySelector) ->
for selector, snippetsByName of snippetsBySelector

View File

@@ -1,5 +1,4 @@
Snippet = require 'snippets/lib/snippet'
LoadSnippetsTask = require 'snippets/lib/load-snippets-task'
RootView = require 'root-view'
Buffer = require 'text-buffer'
Editor = require 'editor'
@@ -12,13 +11,12 @@ describe "Snippets extension", ->
beforeEach ->
window.rootView = new RootView
rootView.open('sample.js')
spyOn(LoadSnippetsTask.prototype, 'start')
packageWithSnippets = window.loadPackage("package-with-snippets")
spyOn(atom, "getLoadedPackages").andCallFake ->
window.textMatePackages.concat([packageWithSnippets])
spyOn(require("snippets/lib/snippets"), 'loadAll')
window.loadPackage("snippets")
editor = rootView.getActiveView()
@@ -239,12 +237,13 @@ describe "Snippets extension", ->
describe "snippet loading", ->
beforeEach ->
jasmine.unspy(LoadSnippetsTask.prototype, 'start')
spyOn(LoadSnippetsTask.prototype, 'loadAtomSnippets').andCallFake -> @snippetsLoaded({})
spyOn(LoadSnippetsTask.prototype, 'loadTextMateSnippets').andCallFake -> @snippetsLoaded({})
jasmine.unspy(window, "setTimeout")
jasmine.unspy(snippets, 'loadAll')
spyOn(snippets, 'loadAtomSnippets').andCallFake (path, done) -> done()
spyOn(snippets, 'loadTextMateSnippets').andCallFake (path, done) -> done()
it "loads non-hidden snippet files from all atom packages with snippets directories, logging a warning if a file can't be parsed", ->
jasmine.unspy(LoadSnippetsTask.prototype, 'loadAtomSnippets')
jasmine.unspy(snippets, 'loadAtomSnippets')
spyOn(console, 'warn')
snippets.loaded = false
snippets.loadAll()
@@ -259,7 +258,7 @@ describe "Snippets extension", ->
expect(console.warn.calls.length).toBe 1
it "loads snippets from all TextMate packages with snippets", ->
jasmine.unspy(LoadSnippetsTask.prototype, 'loadTextMateSnippets')
jasmine.unspy(snippets, 'loadTextMateSnippets')
spyOn(console, 'warn')
snippets.loaded = false
snippets.loadAll()
@@ -281,28 +280,11 @@ describe "Snippets extension", ->
expect(console.warn).toHaveBeenCalled()
expect(console.warn.calls.length).toBe 1
it "terminates the worker when loading completes", ->
jasmine.unspy(LoadSnippetsTask.prototype, 'loadAtomSnippets')
spyOn(console, "warn")
spyOn(Worker.prototype, 'terminate').andCallThrough()
it "loads CSON snippets from TextMate packages", ->
jasmine.unspy(snippets, 'loadTextMateSnippets')
snippets.loaded = false
snippets.loadAll()
waitsFor "all snippets to load", 5000, -> snippets.loaded
runs ->
expect(console.warn).toHaveBeenCalled()
expect(console.warn.argsForCall[0]).toMatch /Error reading snippets file '.*?\/spec\/fixtures\/packages\/package-with-snippets\/snippets\/junk-file'/
expect(Worker.prototype.terminate).toHaveBeenCalled()
expect(Worker.prototype.terminate.calls.length).toBe 1
it "loads CSON snippets from TextMate packages", ->
jasmine.unspy(LoadSnippetsTask.prototype, 'loadTextMateSnippets')
snippets.loaded = false
task = new LoadSnippetsTask(snippets)
task.packages = [Package.build(project.resolve('packages/package-with-a-cson-grammar.tmbundle'))]
task.start()
waitsFor "CSON snippets to load", 5000, -> snippets.loaded
runs ->

View File

@@ -1,25 +1,35 @@
_ = require 'underscore'
fs = require 'fs-utils'
fs = require 'fs'
fsUtils = require 'fs-utils'
module.exports =
isObjectPath: (path) ->
extension = fs.extension(path)
extension = fsUtils.extension(path)
extension is '.cson' or extension is '.json'
readObject: (path) ->
contents = fs.read(path)
if fs.extension(path) is '.cson'
@parseObject(path, fsUtils.read(path))
readObjectAsync: (path, done) ->
fs.readFile path, 'utf8', (err, contents) =>
return done(err) if err?
try done(null, @parseObject(path, contents))
catch err
done(err)
parseObject: (path, contents) ->
if fsUtils.extension(path) is '.cson'
CoffeeScript = require 'coffee-script'
CoffeeScript.eval(contents, bare: true)
else
JSON.parse(contents)
writeObject: (path, object) ->
if fs.extension(path) is '.cson'
if fsUtils.extension(path) is '.cson'
content = @stringify(object)
else
content = JSON.stringify(object, undefined, 2)
fs.write(path, "#{content}\n")
fsUtils.write(path, "#{content}\n")
stringifyIndent: (level=0) -> _.multiplyString(' ', Math.max(level, 0))

View File

@@ -273,3 +273,16 @@ module.exports =
throw new Error(e) if e
object = data[0]
object
readPlistAsync: (path, done) ->
plist = require 'plist'
fs.readFile path, 'utf8', (err, contents) ->
return done(err) if err
[parseErr, object] = []
# plist has an async api, but it isn't actually synchronous
# and it doesn't ever call our callback if there's invalid input
plist.parseString contents, (err, data) ->
parseErr = err
object = data[0] unless err
parseErr = "Could not parse plist at path: '#{path}'" unless object
done(parseErr, object)