Load snippets from CSON/JSON. Use syntax properties for scoping.

This commit eliminates the custom `snippets` format and instead just
uses CSON/JSON.
This commit is contained in:
Corey Johnson & Nathan Sobo 2013-01-07 13:35:08 -07:00
parent 2d4c4b3930
commit 361bf83345
6 changed files with 108 additions and 149 deletions

View File

@ -0,0 +1,34 @@
".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
"""
"Expectation":
prefix: "be"
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]"
"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

@ -1,31 +1,10 @@
{
var Snippet = require('snippets/src/snippet');
var Point = require('point');
body = leadingLines:bodyLineWithNewline* lastLine:bodyLine? {
return lastLine ? leadingLines.concat([lastLine]) : leadingLines;
}
snippets = snippets:snippet+ ws? {
var snippetsByPrefix = {};
snippets.forEach(function(snippet) {
snippetsByPrefix[snippet.prefix] = snippet
});
return snippetsByPrefix;
}
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; }
bodyLineWithNewline = bodyLine:bodyLine '\n' { return bodyLine; }
bodyLine = content:(tabStop / bodyText)* { return content; }
bodyText = text:bodyChar+ { return text.join(''); }
bodyChar = !(end / tabStop) char:[^\n] { return char; }
bodyChar = !(tabStop) char:[^\n] { return char; }
tabStop = simpleTabStop / tabStopWithPlaceholder
simpleTabStop = '$' index:[0-9]+ {
return { index: parseInt(index), placeholderText: '' };
@ -33,7 +12,3 @@ simpleTabStop = '$' index:[0-9]+ {
tabStopWithPlaceholder = '${' index:[0-9]+ ':' placeholderText:[^}]* '}' {
return { index: parseInt(index), placeholderText: placeholderText.join('') };
}
end = 'endsnippet'
ws = ([ \n] / comment)+
comment = '#' [^\n]*

View File

@ -17,36 +17,45 @@ 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:($3)
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} and then here ${2:second}
endsnippet
"""
snippet t5 "Caused problems with undo"
first line$1
${2:placeholder ending second line}
endsnippet
"""
"caused problems with undo":
prefix: "t5"
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", ->
@ -188,56 +197,20 @@ describe "Snippets extension", ->
anotherEditor.trigger keydownEvent('tab', target: anotherEditor[0])
expect(anotherEditor.getSelectedBufferRange()).toEqual [[1, 6], [1, 36]]
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
"""
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)
it "breaks a snippet body into lines, with each line containing tab stops at the appropriate position", ->
bodyTree = Snippets.parser.parse """
go here next:($2) and finally go here:(${3:here!})
go here first:($1)
endsnippet
"""
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 [
[
"go here next:(",
{ index: 2, placeholderText: "" },
") and finally go here:(",
{ index: 3, placeholderText: "here!" },
")",
],
[ "go here first:(", { index: 1, placeholderText: "" }, ")"]
]

View File

@ -3,19 +3,21 @@ 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
for bodyLine, i in bodyTree
lineText = []
for segment in bodyLine
if segment.index

View File

@ -2,32 +2,41 @@ fs = require 'fs'
PEG = require 'pegjs'
_ = require 'underscore'
SnippetExpansion = require 'snippets/src/snippet-expansion'
Snippet = require './snippet'
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 snippetsPath in fs.list(@userSnippetsDir)
@load(snippetsPath)
loadSnippetsFile: (path) ->
@evalSnippets(fs.base(path, '.snippets'), fs.read(path))
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)
evalSnippets: (extension, text) ->
@snippetsByExtension[extension] = @snippetsParser.parse(text)
enableSnippetsInEditor: (editor) ->
editor.command 'snippets:expand', (e) =>
editSession = editor.activeEditSession
prefix = editSession.getLastCursor().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