mirror of
https://github.com/atom/atom.git
synced 2026-01-24 06:18:03 -05:00
Load snippets in a web worker
A single web worker is now used to load snippets one bundle at a time.
This commit is contained in:
@@ -4,14 +4,14 @@ PEG = require 'pegjs'
|
||||
_ = require 'underscore'
|
||||
SnippetExpansion = require './src/snippet-expansion'
|
||||
Snippet = require './src/snippet'
|
||||
require './src/package-extensions'
|
||||
SnippetsTask = require './src/snippets-task'
|
||||
|
||||
module.exports =
|
||||
class Snippets extends AtomPackage
|
||||
|
||||
snippetsByExtension: {}
|
||||
parser: PEG.buildParser(fs.read(require.resolve 'snippets/snippets.pegjs'), trackLineAndColumn: true)
|
||||
userSnippetsDir: fs.join(config.configDirPath, 'snippets')
|
||||
loaded: false
|
||||
|
||||
activate: (@rootView) ->
|
||||
window.snippets = this
|
||||
@@ -19,10 +19,7 @@ class Snippets extends AtomPackage
|
||||
@rootView.on 'editor:attached', (e, editor) => @enableSnippetsInEditor(editor)
|
||||
|
||||
loadAll: ->
|
||||
for pack in atom.getPackages()
|
||||
pack.loadSnippets()
|
||||
|
||||
@loadDirectory(@userSnippetsDir) if fs.exists(@userSnippetsDir)
|
||||
new SnippetsTask(this).start()
|
||||
|
||||
loadDirectory: (snippetsDirPath) ->
|
||||
for snippetsPath in fs.list(snippetsDirPath) when fs.base(snippetsPath).indexOf('.') isnt 0
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
Snippets = require 'snippets'
|
||||
Snippet = require 'snippets/src/snippet'
|
||||
SnippetsTask = require 'snippets/src/snippets-task'
|
||||
RootView = require 'root-view'
|
||||
Buffer = require 'buffer'
|
||||
Editor = require 'editor'
|
||||
@@ -12,8 +13,7 @@ describe "Snippets extension", ->
|
||||
[buffer, editor] = []
|
||||
beforeEach ->
|
||||
rootView = new RootView(require.resolve('fixtures/sample.js'))
|
||||
spyOn(AtomPackage.prototype, 'loadSnippets')
|
||||
spyOn(TextMatePackage.prototype, 'loadSnippets')
|
||||
spyOn(SnippetsTask.prototype, 'start')
|
||||
atom.loadPackage("snippets")
|
||||
editor = rootView.getActiveEditor()
|
||||
buffer = editor.getBuffer()
|
||||
@@ -216,28 +216,36 @@ describe "Snippets extension", ->
|
||||
describe "snippet loading", ->
|
||||
it "loads non-hidden snippet files from all atom packages with snippets directories, logging a warning if a file can't be parsed", ->
|
||||
spyOn(console, 'warn').andCallThrough()
|
||||
jasmine.unspy(AtomPackage.prototype, 'loadSnippets')
|
||||
jasmine.unspy(SnippetsTask.prototype, 'start')
|
||||
snippets.loaded = false
|
||||
snippets.loadAll()
|
||||
|
||||
expect(syntax.getProperty(['.test'], 'snippets.test')?.constructor).toBe Snippet
|
||||
waitsFor((-> snippets.loaded), "Waiting for all snippets to load", 5000)
|
||||
|
||||
# warn about junk-file, but don't even try to parse a hidden file
|
||||
expect(console.warn).toHaveBeenCalled()
|
||||
expect(console.warn.calls.length).toBeGreaterThan 0
|
||||
runs ->
|
||||
expect(syntax.getProperty(['.test'], 'snippets.test')?.constructor).toBe Snippet
|
||||
|
||||
# warn about junk-file, but don't even try to parse a hidden file
|
||||
expect(console.warn).toHaveBeenCalled()
|
||||
expect(console.warn.calls.length).toBeGreaterThan 0
|
||||
|
||||
it "loads snippets from all TextMate packages with snippets", ->
|
||||
jasmine.unspy(TextMatePackage.prototype, 'loadSnippets')
|
||||
jasmine.unspy(SnippetsTask.prototype, 'start')
|
||||
snippets.loaded = false
|
||||
snippets.loadAll()
|
||||
|
||||
snippet = syntax.getProperty(['.source.js'], 'snippets.fun')
|
||||
expect(snippet.constructor).toBe Snippet
|
||||
expect(snippet.prefix).toBe 'fun'
|
||||
expect(snippet.name).toBe 'Function'
|
||||
expect(snippet.body).toBe """
|
||||
function function_name (argument) {
|
||||
\t// body...
|
||||
}
|
||||
"""
|
||||
waitsFor((-> snippets.loaded), "Waiting for all snippets to load", 5000)
|
||||
|
||||
runs ->
|
||||
snippet = syntax.getProperty(['.source.js'], 'snippets.fun')
|
||||
expect(snippet.constructor).toBe Snippet
|
||||
expect(snippet.prefix).toBe 'fun'
|
||||
expect(snippet.name).toBe 'Function'
|
||||
expect(snippet.body).toBe """
|
||||
function function_name (argument) {
|
||||
\t// body...
|
||||
}
|
||||
"""
|
||||
|
||||
describe "Snippets parser", ->
|
||||
it "breaks a snippet body into lines, with each line containing tab stops at the appropriate position", ->
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
AtomPackage = require 'atom-package'
|
||||
TextMatePackage = require 'text-mate-package'
|
||||
fs = require 'fs'
|
||||
|
||||
AtomPackage.prototype.loadSnippets = ->
|
||||
snippetsDirPath = fs.join(@path, 'snippets')
|
||||
snippets.loadDirectory(snippetsDirPath) if fs.exists(snippetsDirPath)
|
||||
|
||||
TextMatePackage.prototype.loadSnippets = ->
|
||||
snippetsDirPath = fs.join(@path, 'Snippets')
|
||||
if fs.exists(snippetsDirPath)
|
||||
tmSnippets = fs.list(snippetsDirPath).map (snippetPath) -> fs.readPlist(snippetPath)
|
||||
snippets.add(@translateSnippets(tmSnippets))
|
||||
|
||||
TextMatePackage.prototype.translateSnippets = (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
|
||||
43
src/packages/snippets/src/snippets-reader.coffee
Normal file
43
src/packages/snippets/src/snippets-reader.coffee
Normal file
@@ -0,0 +1,43 @@
|
||||
eval("window = {};")
|
||||
eval("console = {};")
|
||||
console.warn = ->
|
||||
self.postMessage
|
||||
type: 'warn'
|
||||
details: arguments
|
||||
console.log = ->
|
||||
self.postMessage
|
||||
type: 'warn'
|
||||
details: arguments
|
||||
eval("attachEvent = function(){};")
|
||||
|
||||
self.addEventListener 'message', (event) ->
|
||||
switch event.data.type
|
||||
when 'start'
|
||||
window.resourcePath = event.data.resourcePath
|
||||
importScripts(event.data.requirePath)
|
||||
self.postMessage(type:'started')
|
||||
else
|
||||
self[event.data.type](event.data)
|
||||
|
||||
self.loadTextmateSnippets = ({path}) ->
|
||||
fs = require 'fs'
|
||||
snippetsDirPath = fs.join(path, 'Snippets')
|
||||
snippets = fs.list(snippetsDirPath).map (snippetPath) ->
|
||||
fs.readPlist(snippetPath)
|
||||
self.postMessage
|
||||
type: 'loadSnippets'
|
||||
snippets: snippets
|
||||
|
||||
self.loadAtomSnippets = ({path}) ->
|
||||
fs = require 'fs'
|
||||
snippetsDirPath = fs.join(path, 'snippets')
|
||||
snippets = []
|
||||
for snippetsPath in fs.list(snippetsDirPath)
|
||||
continue if fs.base(snippetsPath).indexOf('.') is 0
|
||||
try
|
||||
snippets.push(fs.readObject(snippetsPath))
|
||||
catch e
|
||||
console.warn "Error reading snippets file '#{snippetsPath}'"
|
||||
self.postMessage
|
||||
type: 'loadSnippets'
|
||||
snippets: snippets
|
||||
42
src/packages/snippets/src/snippets-task.coffee
Normal file
42
src/packages/snippets/src/snippets-task.coffee
Normal file
@@ -0,0 +1,42 @@
|
||||
Task = require 'Task'
|
||||
TextMatePackage = require 'text-mate-package'
|
||||
|
||||
module.exports =
|
||||
class SnippetsTask extends Task
|
||||
|
||||
constructor: (@snippets) ->
|
||||
super('snippets/src/snippets-reader')
|
||||
|
||||
@packages = atom.getPackages()
|
||||
@packages.push(path: config.configDirPath)
|
||||
|
||||
onProgress: (event) =>
|
||||
if event.data.type is 'loadSnippets'
|
||||
rawSnippets = event.data.snippets
|
||||
if @package instanceof TextMatePackage
|
||||
@snippets.add(@translateTextmateSnippets(rawSnippets))
|
||||
else
|
||||
@snippets.add(snippet) for snippet in rawSnippets
|
||||
|
||||
@package = @packages.shift()
|
||||
if not @package?
|
||||
@snippets.loaded = true
|
||||
return
|
||||
|
||||
if @package instanceof TextMatePackage
|
||||
eventType = 'loadTextmateSnippets'
|
||||
else
|
||||
eventType = 'loadAtomSnippets'
|
||||
{ type: eventType, path: @package.path }
|
||||
|
||||
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
|
||||
@@ -2,7 +2,6 @@
|
||||
# http://ringojs.org/api/v0.8/fs/
|
||||
|
||||
_ = require 'underscore'
|
||||
$ = require 'jquery'
|
||||
|
||||
module.exports =
|
||||
# Make the given path absolute by resolving it against the
|
||||
|
||||
@@ -30,12 +30,12 @@ require = (path, cb) ->
|
||||
|
||||
if __moduleExists file
|
||||
if not __modules.loaded[file.toLowerCase()]?
|
||||
console.warn "Circular require: #{__filename} required #{file}"
|
||||
console.warn "Circular require: #{window.__filename} required #{file}"
|
||||
return __modules[file]
|
||||
else if __modules.loaded[file.toLowerCase()]
|
||||
console.warn "Multiple requires (different cases) for #{file}"
|
||||
|
||||
[ previousFilename, window.__filename ] = [ __filename, file ]
|
||||
[ previousFilename, window.__filename ] = [ window.__filename, file ]
|
||||
__modules[file] = {} # Fix for circular references
|
||||
__modules[file] = (exts[ext] or (file) -> __read file) file
|
||||
window.__filename = previousFilename
|
||||
@@ -43,10 +43,10 @@ require = (path, cb) ->
|
||||
|
||||
define = (cb) ->
|
||||
__defines.push ->
|
||||
exports = __modules[__filename] or {}
|
||||
exports = __modules[window.__filename] or {}
|
||||
module = exports: exports
|
||||
cb.call exports, require, exports, module
|
||||
__modules.loaded[__filename.toLowerCase()] = true
|
||||
__modules.loaded[window.__filename.toLowerCase()] = true
|
||||
module.exports or exports
|
||||
|
||||
exts =
|
||||
@@ -57,6 +57,19 @@ exts =
|
||||
coffee: (file) ->
|
||||
exts.js(file, __coffeeCache(file))
|
||||
|
||||
getPath = (path) ->
|
||||
path = resolve(path)
|
||||
parts = path.split '.'
|
||||
return path unless parts[parts.length - 1] is 'coffee'
|
||||
|
||||
tmpPath = "/tmp/atom-compiled-scripts"
|
||||
cachePath = [tmpPath, $native.md5ForPath(path)].join("/")
|
||||
if not __exists(cachePath)
|
||||
{CoffeeScript} = require 'coffee-script'
|
||||
compiled = CoffeeScript.compile(__read(path), filename: path)
|
||||
$native.write(cachePath, compiled)
|
||||
cachePath
|
||||
|
||||
resolve = (name, {verifyExistence}={}) ->
|
||||
verifyExistence ?= true
|
||||
file = name
|
||||
@@ -65,11 +78,11 @@ resolve = (name, {verifyExistence}={}) ->
|
||||
file = parts[parts.length-1]
|
||||
|
||||
if file[0..1] is './'
|
||||
prefix = __filename.split('/')[0..-2].join '/'
|
||||
prefix = window.__filename.split('/')[0..-2].join '/'
|
||||
file = file.replace './', "#{prefix}/"
|
||||
|
||||
if file[0..2] is '../'
|
||||
prefix = __filename.split('/')[0..-3].join '/'
|
||||
prefix = window.__filename.split('/')[0..-3].join '/'
|
||||
file = file.replace '../', "#{prefix}/"
|
||||
|
||||
if file[0] isnt '/'
|
||||
@@ -157,6 +170,7 @@ this.nakedLoad = nakedLoad
|
||||
this.define = define
|
||||
|
||||
this.require.paths = paths
|
||||
this.require.getPath = getPath
|
||||
this.require.exts = exts
|
||||
|
||||
this.require.resolve = resolve
|
||||
|
||||
26
src/stdlib/task.coffee
Normal file
26
src/stdlib/task.coffee
Normal file
@@ -0,0 +1,26 @@
|
||||
module.exports =
|
||||
class Task
|
||||
|
||||
constructor: (path) ->
|
||||
@path = require.getPath(path)
|
||||
|
||||
onProgress: (event) ->
|
||||
|
||||
start: ->
|
||||
worker = new Worker(@path)
|
||||
worker.onmessage = (event) =>
|
||||
switch event.data.type
|
||||
when 'warn'
|
||||
console.warn(event.data.details...)
|
||||
return
|
||||
when 'log'
|
||||
console.log(event.data.details...)
|
||||
return
|
||||
|
||||
reply = @onProgress(event)
|
||||
worker.postMessage(reply) if reply
|
||||
|
||||
worker.postMessage
|
||||
type: 'start'
|
||||
resourcePath: window.resourcePath
|
||||
requirePath: require.getPath('require')
|
||||
Reference in New Issue
Block a user