Merge branch 'snippets' into dev

Conflicts:
	src/app/package.coffee
	src/packages/snippets/src/snippets.coffee
This commit is contained in:
Nathan Sobo 2013-01-09 13:10:11 -07:00
commit 2c4da1b8dd
17 changed files with 324 additions and 237 deletions

View File

@ -0,0 +1,44 @@
".source.coffee":
"Describe block":
prefix: "de"
body: """
describe "${1:description}", ->
${2:body}
"""
"It block":
prefix: "i"
body: """
it "$1", ->
$2
"""
"Before each":
prefix: "be"
body: """
beforeEach ->
$1
"""
"After each":
prefix: "af"
body: """
afterEach ->
$1
"""
"Expectation":
prefix: "ex"
body: "expect($1).to$2"
"Console log":
prefix: "log"
body: "console.log $1"
"Range array":
prefix: "ra"
body: "[[$1, $2], [$3, $4]]"
"Point array":
prefix: "pt"
body: "[$1, $2]"
"Key-value pair":
prefix: ":"
body: '${1:"${2:key}"}: ${3:value}'
"Create Jasmine spy":
prefix: "pt"
body: 'jasmine.createSpy("${1:description}")$2'

View File

@ -1,34 +0,0 @@
snippet de "Describe block"
describe "${1:description}", ->
${2:body}
endsnippet
snippet i "It block"
it "$1", ->
$2
endsnippet
snippet be "Before each"
beforeEach ->
$1
endsnippet
snippet ex "Expectation"
expect($1).to$2
endsnippet
snippet log "Console log"
console.log $1
endsnippet
snippet ra "Range array"
[[$1, $2], [$3, $4]]
endsnippet
snippet pt "Point array"
[$1, $2]
endsnippet
snippet spy "Jasmine spy"
jasmine.createSpy("${1:description}")$2
endsnippet

View File

@ -0,0 +1,4 @@
".test":
"Test Snippet":
prefix: "test"
body: "testing 123"

View File

@ -13,11 +13,12 @@ TokenizedBuffer = require 'tokenized-buffer'
fs = require 'fs'
require 'window'
requireStylesheet "jasmine.css"
require.paths.unshift(require.resolve('fixtures/packages'))
fixturePackagesPath = require.resolve('fixtures/packages')
require.paths.unshift(fixturePackagesPath)
[bindingSetsToRestore, bindingSetsByFirstKeystrokeToRestore] = []
# Load TextMate bundles, which specs rely on (but not other packages)
atom.loadPackages(atom.getAvailableTextMateBundles())
atom.loadTextMatePackages()
beforeEach ->
window.fixturesProject = new Project(require.resolve('fixtures'))
@ -29,6 +30,7 @@ beforeEach ->
# reset config before each spec; don't load or save from/to `config.json`
window.config = new Config()
config.packageDirPaths.unshift(fixturePackagesPath)
spyOn(config, 'load')
spyOn(config, 'save')
config.set "editor.fontSize", 16

View File

@ -1,4 +1,7 @@
Range = require 'range'
EventEmitter = require 'event-emitter'
Subscriber = require 'subscriber'
_ = require 'underscore'
module.exports =
class AnchorRange
@ -6,11 +9,14 @@ class AnchorRange
end: null
buffer: null
editSession: null # optional
destroyed: false
constructor: (bufferRange, @buffer, @editSession) ->
bufferRange = Range.fromObject(bufferRange)
@startAnchor = @buffer.addAnchorAtPosition(bufferRange.start, ignoreChangesStartingOnAnchor: true)
@endAnchor = @buffer.addAnchorAtPosition(bufferRange.end)
@subscribe @startAnchor, 'destroyed', => @destroy()
@subscribe @endAnchor, 'destroyed', => @destroy()
getBufferRange: ->
new Range(@startAnchor.getBufferPosition(), @endAnchor.getBufferPosition())
@ -22,7 +28,14 @@ class AnchorRange
@getBufferRange().containsPoint(bufferPosition)
destroy: ->
return if @destroyed
@unsubscribe()
@startAnchor.destroy()
@endAnchor.destroy()
@buffer.removeAnchorRange(this)
@editSession?.removeAnchorRange(this)
@destroyed = true
@trigger 'destroyed'
_.extend(AnchorRange.prototype, EventEmitter)
_.extend(AnchorRange.prototype, Subscriber)

View File

@ -10,6 +10,7 @@ class Anchor
screenPosition: null
ignoreChangesStartingOnAnchor: false
strong: false
destroyed: false
constructor: (@buffer, options = {}) ->
{ @editSession, @ignoreChangesStartingOnAnchor, @strong } = options
@ -81,8 +82,10 @@ class Anchor
@setScreenPosition(screenPosition, bufferChange: options.bufferChange, clip: false, assignBufferPosition: false, autoscroll: options.autoscroll)
destroy: ->
return if @destroyed
@buffer.removeAnchor(this)
@editSession?.removeAnchor(this)
@destroyed = true
@trigger 'destroyed'
_.extend(Anchor.prototype, EventEmitter)

View File

@ -9,15 +9,18 @@ class AtomPackage extends Package
constructor: (@name) ->
super
@keymapsDirPath = fs.join(@path, 'keymaps')
if @requireModule
@module = require(@path)
@module.name = @name
load: ->
@loadMetadata()
@loadKeymaps()
@loadStylesheets()
rootView.activatePackage(@name, @module) if @module
try
if @requireModule
@module = require(@path)
@module.name = @name
@loadMetadata()
@loadKeymaps()
@loadStylesheets()
rootView.activatePackage(@name, @module) if @module
catch e
console.warn "Failed to load package named '#{@name}'", e.stack
loadMetadata: ->
if metadataPath = fs.resolveExtension(fs.join(@path, "package"), ['cson', 'json'])

View File

@ -12,7 +12,23 @@ _.extend atom,
pendingBrowserProcessCallbacks: {}
getAvailablePackages: ->
loadPackages: ->
pack.load() for pack in @getPackages()
getPackages: ->
@getPackageNames().map (name) -> Package.build(name)
loadTextMatePackages: ->
pack.load() for pack in @getTextMatePackages()
getTextMatePackages: ->
@getPackages().filter (pack) -> pack instanceof TextMatePackage
loadPackage: (name) ->
Package.build(name).load()
getPackageNames: ->
disabledPackages = config.get("core.disabledPackages") ? []
allPackageNames = []
for packageDirPath in config.packageDirPaths
packageNames = fs.list(packageDirPath)
@ -20,17 +36,7 @@ _.extend atom,
.map((packagePath) -> fs.base(packagePath))
allPackageNames.push(packageNames...)
_.unique(allPackageNames)
getAvailableTextMateBundles: ->
@getAvailablePackages().filter (packageName) => TextMatePackage.testName(packageName)
loadPackages: (packageNames=@getAvailablePackages()) ->
disabledPackages = config.get("core.disabledPackages") ? []
for packageName in packageNames
@loadPackage(packageName) unless _.contains(disabledPackages, packageName)
loadPackage: (name) ->
Package.load(name)
.filter (name) -> not _.contains(disabledPackages, name)
loadThemes: ->
themeNames = config.get("core.themes") ? ['IR_Black']

View File

@ -2,18 +2,13 @@ fs = require 'fs'
module.exports =
class Package
@load: (name) ->
@build: (name) ->
AtomPackage = require 'atom-package'
TextMatePackage = require 'text-mate-package'
try
if TextMatePackage.testName(name)
new TextMatePackage(name).load()
else
new AtomPackage(name).load()
catch e
console.warn "Failed to load package named '#{name}'", e.stack
if TextMatePackage.testName(name)
new TextMatePackage(name)
else
new AtomPackage(name)
name: null
path: null
@ -29,10 +24,3 @@ class Package
else
@requireModule = true
@path = fs.directory(@path)
load: ->
for grammar in @getGrammars()
syntax.addGrammar(grammar)
for { selector, properties } in @getScopedProperties()
syntax.addProperties(selector, properties)

View File

@ -23,6 +23,16 @@ class TextMatePackage extends Package
@preferencesPath = fs.join(@path, "Preferences")
@syntaxesPath = fs.join(@path, "Syntaxes")
load: ->
try
for grammar in @getGrammars()
syntax.addGrammar(grammar)
for { selector, properties } in @getScopedProperties()
syntax.addProperties(selector, properties)
catch e
console.warn "Failed to load package named '#{@name}'", e.stack
getGrammars: ->
return @grammars if @grammars
@grammars = []

View File

@ -1,39 +1,15 @@
{
var Snippet = require('snippets/src/snippet');
var Point = require('point');
}
bodyContent = content:(tabStop / bodyContentText)* { return content; }
bodyContentText = text:bodyContentChar+ { return text.join(''); }
bodyContentChar = !tabStop char:. { return char; }
snippets = snippets:snippet+ ws? {
var snippetsByPrefix = {};
snippets.forEach(function(snippet) {
snippetsByPrefix[snippet.prefix] = snippet
});
return snippetsByPrefix;
}
placeholderContent = content:(tabStop / placeholderContentText)* { return content; }
placeholderContentText = text:placeholderContentChar+ { return text.join(''); }
placeholderContentChar = !tabStop char:[^}] { return char; }
snippet = ws? start ws prefix:prefix ws description:string bodyPosition:beforeBody body:body end {
return new Snippet({ bodyPosition: bodyPosition, prefix: prefix, description: description, body: body });
}
start = 'snippet'
prefix = prefix:[A-Za-z0-9_]+ { return prefix.join(''); }
string = ['] body:[^']* ['] { return body.join(''); }
/ ["] body:[^"]* ["] { return body.join(''); }
beforeBody = [ ]* '\n' { return new Point(line, 0); } // return start position of body: body begins on next line, so don't subtract 1 from line
body = bodyLine+
bodyLine = content:(tabStop / bodyText)* '\n' { return content; }
bodyText = text:bodyChar+ { return text.join(''); }
bodyChar = !(end / tabStop) char:[^\n] { return char; }
tabStop = simpleTabStop / tabStopWithPlaceholder
simpleTabStop = '$' index:[0-9]+ {
return { index: parseInt(index), placeholderText: '' };
return { index: parseInt(index), content: [] };
}
tabStopWithPlaceholder = '${' index:[0-9]+ ':' placeholderText:[^}]* '}' {
return { index: parseInt(index), placeholderText: placeholderText.join('') };
tabStopWithPlaceholder = '${' index:[0-9]+ ':' content:placeholderContent '}' {
return { index: parseInt(index), content: content };
}
end = 'endsnippet'
ws = ([ \n] / comment)+
comment = '#' [^\n]*

View File

@ -1,14 +1,19 @@
Snippets = require 'snippets'
Snippet = require 'snippets/src/snippet'
RootView = require 'root-view'
Buffer = require 'buffer'
Editor = require 'editor'
_ = require 'underscore'
fs = require 'fs'
AtomPackage = require 'atom-package'
TextMatePackage = require 'text-mate-package'
describe "Snippets extension", ->
[buffer, editor] = []
beforeEach ->
rootView = new RootView(require.resolve('fixtures/sample.js'))
spyOn(AtomPackage.prototype, 'loadSnippets')
spyOn(TextMatePackage.prototype, 'loadSnippets')
atom.loadPackage("snippets")
editor = rootView.getActiveEditor()
buffer = editor.getBuffer()
@ -17,36 +22,50 @@ describe "Snippets extension", ->
afterEach ->
rootView.remove()
delete window.snippets
describe "when 'tab' is triggered on the editor", ->
beforeEach ->
Snippets.evalSnippets 'js', """
snippet t1 "Snippet without tab stops"
this is a test
endsnippet
snippets.add
".source.js":
"without tab stops":
prefix: "t1"
body: "this is a test"
snippet t2 "With tab stops"
go here next:($2) and finally go here:($3)
go here first:($1)
"tab stops":
prefix: "t2"
body: """
go here next:($2) and finally go here:($0)
go here first:($1)
endsnippet
"""
snippet t3 "With indented second line"
line 1
line 2$1
"indented second line":
prefix: "t3"
body: """
line 1
line 2$1
endsnippet
"""
snippet t4 "With tab stop placeholders"
go here ${1:first} and then here ${2:second}
"tab stop placeholders":
prefix: "t4"
body: """
go here ${1:first
think a while}, and then here ${2:second}
endsnippet
"""
snippet t5 "Caused problems with undo"
first line$1
${2:placeholder ending second line}
endsnippet
"""
"nested tab stops":
prefix: "t5"
body: '${1:"${2:key}"}: ${3:value}'
"caused problems with undo":
prefix: "t6"
body: """
first line$1
${2:placeholder ending second line}
"""
describe "when the letters preceding the cursor trigger a snippet", ->
describe "when the snippet contains no tab stops", ->
@ -98,8 +117,22 @@ describe "Snippets extension", ->
it "auto-fills the placeholder text and highlights it when navigating to that tab stop", ->
editor.insertText 't4'
editor.trigger 'snippets:expand'
expect(buffer.lineForRow(0)).toBe 'go here first and then here second'
expect(editor.getSelectedBufferRange()).toEqual [[0, 8], [0, 13]]
expect(buffer.lineForRow(0)).toBe 'go here first'
expect(buffer.lineForRow(1)).toBe 'think a while, and then here second'
expect(editor.getSelectedBufferRange()).toEqual [[0, 8], [1, 13]]
editor.trigger keydownEvent('tab', target: editor[0])
expect(editor.getSelectedBufferRange()).toEqual [[1, 29], [1, 35]]
describe "when tab stops are nested", ->
it "destroys the inner tab stop if the outer tab stop is modified", ->
buffer.setText('')
editor.insertText 't5'
editor.trigger 'snippets:expand'
expect(buffer.lineForRow(0)).toBe '"key": value'
expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 5]]
editor.insertText("foo")
editor.trigger keydownEvent('tab', target: editor[0])
expect(editor.getSelectedBufferRange()).toEqual [[0, 5], [0, 10]]
describe "when the cursor is moved beyond the bounds of a tab stop", ->
it "terminates the snippet", ->
@ -152,92 +185,68 @@ describe "Snippets extension", ->
describe "when a previous snippet expansion has just been undone", ->
it "expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", ->
editor.insertText 't5\n'
editor.insertText 't6\n'
editor.setCursorBufferPosition [0, 2]
editor.trigger keydownEvent('tab', target: editor[0])
expect(buffer.lineForRow(0)).toBe "first line"
editor.undo()
expect(buffer.lineForRow(0)).toBe "t5"
expect(buffer.lineForRow(0)).toBe "t6"
editor.trigger keydownEvent('tab', target: editor[0])
expect(buffer.lineForRow(0)).toBe "first line"
describe "when a snippet expansion is undone and redone", ->
it "recreates the snippet's tab stops", ->
editor.insertText ' t5\n'
editor.insertText ' t6\n'
editor.setCursorBufferPosition [0, 6]
editor.trigger keydownEvent('tab', target: editor[0])
expect(buffer.lineForRow(0)).toBe " first line"
editor.undo()
editor.redo()
expect(editor.getCursorBufferPosition()).toEqual [0, 14]
editor.trigger keydownEvent('tab', target: editor[0])
expect(editor.getSelectedBufferRange()).toEqual [[1, 6], [1, 36]]
it "restores tabs stops in active edit session even when the initial expansion was in a different edit session", ->
anotherEditor = editor.splitRight()
describe "snippet loading", ->
it "loads snippets from all atom packages with a snippets directory", ->
jasmine.unspy(AtomPackage.prototype, 'loadSnippets')
snippets.loadAll()
editor.insertText ' t5\n'
editor.setCursorBufferPosition [0, 6]
editor.trigger keydownEvent('tab', target: editor[0])
expect(buffer.lineForRow(0)).toBe " first line"
editor.undo()
expect(syntax.getProperty(['.test'], 'snippets.test')?.constructor).toBe Snippet
anotherEditor.redo()
expect(anotherEditor.getCursorBufferPosition()).toEqual [0, 14]
anotherEditor.trigger keydownEvent('tab', target: anotherEditor[0])
expect(anotherEditor.getSelectedBufferRange()).toEqual [[1, 6], [1, 36]]
it "loads snippets from all TextMate packages with snippets", ->
jasmine.unspy(TextMatePackage.prototype, 'loadSnippets')
snippets.loadAll()
describe ".loadSnippetsFile(path)", ->
it "loads the snippets in the given file", ->
spyOn(fs, 'read').andReturn """
snippet t1 "Test snippet 1"
this is a test 1
endsnippet
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...
}
"""
Snippets.loadSnippetsFile('/tmp/foo/js.snippets')
expect(fs.read).toHaveBeenCalledWith('/tmp/foo/js.snippets')
editor.insertText("t1")
editor.trigger 'snippets:expand'
expect(buffer.lineForRow(0)).toBe "this is a test 1var quicksort = function () {"
describe "Snippets parser", ->
it "can parse multiple snippets", ->
snippets = Snippets.snippetsParser.parse """
snippet t1 "Test snippet 1"
this is a test 1
endsnippet
snippet t2 "Test snippet 2"
this is a test 2
endsnippet
"""
expect(_.keys(snippets).length).toBe 2
snippet = snippets['t1']
expect(snippet.prefix).toBe 't1'
expect(snippet.description).toBe "Test snippet 1"
expect(snippet.body).toBe "this is a test 1"
snippet = snippets['t2']
expect(snippet.prefix).toBe 't2'
expect(snippet.description).toBe "Test snippet 2"
expect(snippet.body).toBe "this is a test 2"
it "can parse snippets with tabstops", ->
snippets = Snippets.snippetsParser.parse """
# this line intentially left blank.
snippet t1 "Snippet with tab stops"
go here next:($2) and finally go here:($3)
go here first:($1)
endsnippet
it "breaks a snippet body into lines, with each line containing tab stops at the appropriate position", ->
bodyTree = Snippets.parser.parse """
the quick brown $1fox ${2:jumped ${3:over}
}the ${4:lazy} dog
"""
snippet = snippets['t1']
expect(snippet.body).toBe """
go here next:() and finally go here:()
go here first:()
"""
expect(snippet.tabStops).toEqual [[[1, 15], [1, 15]], [[0, 14], [0, 14]], [[0, 37], [0, 37]]]
expect(bodyTree).toEqual [
"the quick brown ",
{ index: 1, content: [] },
"fox ",
{
index: 2,
content: [
"jumped ",
{ index: 3, content: ["over"]},
"\n"
],
}
"the "
{ index: 4, content: ["lazy"] },
" dog"
]

View File

@ -0,0 +1,27 @@
AtomPackage = require 'atom-package'
TextMatePackage = require 'text-mate-package'
fs = require 'fs'
AtomPackage.prototype.loadSnippets = ->
snippetsDirPath = fs.join(@path, 'snippets')
if fs.exists(snippetsDirPath)
for snippetsPath in fs.list(snippetsDirPath)
snippets.load(snippetsPath)
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

View File

@ -1,33 +1,44 @@
Subscriber = require 'subscriber'
_ = require 'underscore'
module.exports =
class SnippetExpansion
snippet: null
tabStopAnchorRanges: null
settingTabStop: false
constructor: (snippet, @editSession) ->
constructor: (@snippet, @editSession) ->
@editSession.selectToBeginningOfWord()
startPosition = @editSession.getCursorBufferPosition()
@editSession.transact =>
@editSession.insertText(snippet.body, autoIndent: false)
if snippet.tabStops.length
@placeTabStopAnchorRanges(startPosition, snippet.tabStops)
if snippet.lineCount > 1
@indentSubsequentLines(startPosition.row, snippet)
editSession.pushOperation
do: =>
@subscribe @editSession, 'cursor-moved.snippet-expansion', (e) => @cursorMoved(e)
@placeTabStopAnchorRanges(startPosition, snippet.tabStops)
@editSession.snippetExpansion = this
undo: => @destroy()
@indentSubsequentLines(startPosition.row, snippet) if snippet.lineCount > 1
@editSession.on 'cursor-moved.snippet-expansion', ({oldBufferPosition, newBufferPosition}) =>
return if @settingTabStop
cursorMoved: ({oldBufferPosition, newBufferPosition}) ->
return if @settingTabStop
oldTabStops = @tabStopsForBufferPosition(oldBufferPosition)
newTabStops = @tabStopsForBufferPosition(newBufferPosition)
oldTabStops = @tabStopsForBufferPosition(oldBufferPosition)
newTabStops = @tabStopsForBufferPosition(newBufferPosition)
@destroy() unless _.intersect(oldTabStops, newTabStops).length
@destroy() unless _.intersect(oldTabStops, newTabStops).length
placeTabStopAnchorRanges: (startPosition, tabStopRanges) ->
return unless @snippet.tabStops.length > 0
@tabStopAnchorRanges = tabStopRanges.map ({start, end}) =>
@editSession.addAnchorRange([startPosition.add(start), startPosition.add(end)])
anchorRange = @editSession.addAnchorRange([startPosition.add(start), startPosition.add(end)])
@subscribe anchorRange, 'destroyed', =>
_.remove(@tabStopAnchorRanges, anchorRange)
anchorRange
@setTabStopIndex(0)
indentSubsequentLines: (startRow, snippet) ->
initialIndent = @editSession.lineForBufferRow(startRow).match(/^\s*/)[0]
for row in [startRow + 1...startRow + snippet.lineCount]
@ -68,11 +79,13 @@ class SnippetExpansion
_.intersection(@tabStopAnchorRanges, @editSession.anchorRangesForBufferPosition(bufferPosition))
destroy: ->
@unsubscribe()
anchorRange.destroy() for anchorRange in @tabStopAnchorRanges
@editSession.off '.snippet-expansion'
@editSession.snippetExpansion = null
restore: (@editSession) ->
@editSession.snippetExpansion = this
@tabStopAnchorRanges = @tabStopAnchorRanges.map (anchorRange) =>
@editSession.addAnchorRange(anchorRange.getBufferRange())
_.extend(SnippetExpansion.prototype, Subscriber)

View File

@ -3,34 +3,41 @@ Range = require 'range'
module.exports =
class Snippet
name: null
prefix: null
body: null
lineCount: null
tabStops: null
constructor: ({@bodyPosition, @prefix, @description, body}) ->
@body = @extractTabStops(body)
constructor: ({@name, @prefix, bodyTree}) ->
@body = @extractTabStops(bodyTree)
extractTabStops: (bodyLines) ->
extractTabStops: (bodyTree) ->
tabStopsByIndex = {}
bodyText = []
[row, column] = [0, 0]
for bodyLine, i in bodyLines
lineText = []
for segment in bodyLine
if segment.index
{ index, placeholderText } = segment
tabStopsByIndex[index] = new Range([row, column], [row, column + placeholderText.length])
lineText.push(placeholderText)
else
lineText.push(segment)
column += segment.length
bodyText.push(lineText.join(''))
row++; column = 0
@lineCount = row
# recursive helper function; mutates vars above
extractTabStops = (bodyTree) ->
for segment in bodyTree
if segment.index?
{ index, content } = segment
index = Infinity if index == 0
start = [row, column]
extractTabStops(content)
tabStopsByIndex[index] = new Range(start, [row, column])
else if _.isString(segment)
bodyText.push(segment)
segmentLines = segment.split('\n')
column += segmentLines.shift().length
while nextLine = segmentLines.shift()
row += 1
column = nextLine.length
extractTabStops(bodyTree)
@lineCount = row + 1
@tabStops = []
for index in _.keys(tabStopsByIndex).sort()
@tabStops.push tabStopsByIndex[index]
bodyText.join('\n')
bodyText.join('')

View File

@ -2,38 +2,46 @@ fs = require 'fs'
PEG = require 'pegjs'
_ = require 'underscore'
SnippetExpansion = require 'snippets/src/snippet-expansion'
Snippet = require './snippet'
require './package-extensions'
module.exports =
name: 'Snippets'
snippetsByExtension: {}
snippetsParser: PEG.buildParser(fs.read(require.resolve 'snippets/snippets.pegjs'), trackLineAndColumn: true)
parser: PEG.buildParser(fs.read(require.resolve 'snippets/snippets.pegjs'), trackLineAndColumn: true)
userSnippetsDir: fs.join(config.configDirPath, 'snippets')
activate: (@rootView) ->
@loadSnippets()
window.snippets = this
@loadAll()
@rootView.on 'editor:attached', (e, editor) => @enableSnippetsInEditor(editor)
loadSnippets: ->
snippetsDir = fs.join(config.configDirPath, 'snippets')
if fs.exists(snippetsDir)
@loadSnippetsFile(path) for path in fs.list(snippetsDir) when fs.extension(path) == '.snippets'
loadAll: ->
for pack in atom.getPackages()
pack.loadSnippets()
loadSnippetsFile: (path) ->
@evalSnippets(fs.base(path, '.snippets'), fs.read(path))
for snippetsPath in fs.list(@userSnippetsDir)
@load(snippetsPath)
evalSnippets: (extension, text) ->
@snippetsByExtension[extension] = @snippetsParser.parse(text)
load: (snippetsPath) ->
@add(fs.readObject(snippetsPath))
add: (snippetsBySelector) ->
for selector, snippetsByName of snippetsBySelector
snippetsByPrefix = {}
for name, attributes of snippetsByName
{ prefix, body } = attributes
bodyTree = @parser.parse(body)
snippet = new Snippet({name, prefix, bodyTree})
snippetsByPrefix[snippet.prefix] = snippet
syntax.addProperties(selector, snippets: snippetsByPrefix)
enableSnippetsInEditor: (editor) ->
editor.command 'snippets:expand', (e) =>
editSession = editor.activeEditSession
prefix = editSession.getCursor().getCurrentWordPrefix()
if snippet = @snippetsByExtension[editSession.getFileExtension()]?[prefix]
if snippet = syntax.getProperty(editSession.getCursorScopes(), "snippets.#{prefix}")
editSession.transact ->
snippetExpansion = new SnippetExpansion(snippet, editSession)
editSession.snippetExpansion = snippetExpansion
editSession.pushOperation
undo: -> snippetExpansion.destroy()
redo: (editSession) -> snippetExpansion.restore(editSession)
new SnippetExpansion(snippet, editSession)
else
e.abortKeyBinding()

View File

@ -184,3 +184,11 @@ module.exports =
CoffeeScript.eval(contents, bare: true)
else
JSON.parse(contents)
readPlist: (path) ->
plist = require 'plist'
object = null
plist.parseString @read(path), (e, data) ->
throw new Error(e) if e
object = data[0]
object