From 361bf8334533889e160ea55efacde126122035bb Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Mon, 7 Jan 2013 13:35:08 -0700 Subject: [PATCH] Load snippets from CSON/JSON. Use `syntax` properties for scoping. This commit eliminates the custom `snippets` format and instead just uses CSON/JSON. --- .atom/snippets/coffee.cson | 34 ++++++ .atom/snippets/coffee.snippets | 34 ------ src/packages/snippets/snippets.pegjs | 35 +----- .../snippets/spec/snippets-spec.coffee | 111 +++++++----------- src/packages/snippets/src/snippet.coffee | 10 +- src/packages/snippets/src/snippets.coffee | 33 ++++-- 6 files changed, 108 insertions(+), 149 deletions(-) create mode 100644 .atom/snippets/coffee.cson delete mode 100644 .atom/snippets/coffee.snippets diff --git a/.atom/snippets/coffee.cson b/.atom/snippets/coffee.cson new file mode 100644 index 000000000..cf7e223a9 --- /dev/null +++ b/.atom/snippets/coffee.cson @@ -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' diff --git a/.atom/snippets/coffee.snippets b/.atom/snippets/coffee.snippets deleted file mode 100644 index 57b3e3980..000000000 --- a/.atom/snippets/coffee.snippets +++ /dev/null @@ -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 diff --git a/src/packages/snippets/snippets.pegjs b/src/packages/snippets/snippets.pegjs index 8d3ea766f..ac132cdd2 100644 --- a/src/packages/snippets/snippets.pegjs +++ b/src/packages/snippets/snippets.pegjs @@ -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]* diff --git a/src/packages/snippets/spec/snippets-spec.coffee b/src/packages/snippets/spec/snippets-spec.coffee index 6a1688d4d..e832851e0 100644 --- a/src/packages/snippets/spec/snippets-spec.coffee +++ b/src/packages/snippets/spec/snippets-spec.coffee @@ -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: "" }, ")"] + ] diff --git a/src/packages/snippets/src/snippet.coffee b/src/packages/snippets/src/snippet.coffee index bcdbd3de5..3d8248d3b 100644 --- a/src/packages/snippets/src/snippet.coffee +++ b/src/packages/snippets/src/snippet.coffee @@ -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 diff --git a/src/packages/snippets/src/snippets.coffee b/src/packages/snippets/src/snippets.coffee index fc77ddf59..4d88dc0c5 100644 --- a/src/packages/snippets/src/snippets.coffee +++ b/src/packages/snippets/src/snippets.coffee @@ -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