mirror of
https://github.com/atom/atom.git
synced 2026-01-23 05:48:10 -05:00
Replace snippet loader task w/ async loading
This commit is contained in:
committed by
Corey Johnson & Nathan Sobo
parent
b01a3ff9cc
commit
e5436974eb
@@ -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": {
|
||||
|
||||
@@ -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]
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -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 ->
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user