mirror of
https://github.com/atom/atom.git
synced 2026-04-28 03:01:47 -04:00
Pull out snippets package into a separate repo
This commit is contained in:
@@ -82,6 +82,7 @@
|
||||
"link": "0.1.0",
|
||||
"markdown-preview": "0.1.0",
|
||||
"package-generator": "0.1.0",
|
||||
"snippets": "0.1.0",
|
||||
"spell-check": "0.1.0",
|
||||
"status-bar": "0.1.0",
|
||||
"symbols-view": "0.1.0",
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
'.editor':
|
||||
'tab': 'snippets:expand'
|
||||
@@ -1,6 +0,0 @@
|
||||
# it's critical that these bindings be loaded after those snippets-1 so they
|
||||
# are later in the cascade, hence breaking the keymap into 2 files
|
||||
|
||||
'.editor':
|
||||
'tab': 'snippets:next-tab-stop'
|
||||
'shift-tab': 'snippets:previous-tab-stop'
|
||||
@@ -1,4 +0,0 @@
|
||||
PEG = require 'pegjs'
|
||||
fsUtils = require 'fs-utils'
|
||||
grammarSrc = fsUtils.read(require.resolve('./snippet-body.pegjs'))
|
||||
module.exports = PEG.buildParser(grammarSrc, trackLineAndColumn: true)
|
||||
@@ -1,21 +0,0 @@
|
||||
bodyContent = content:(tabStop / bodyContentText)* { return content; }
|
||||
bodyContentText = text:bodyContentChar+ { return text.join(''); }
|
||||
bodyContentChar = !tabStop char:. { return char; }
|
||||
|
||||
tabStop = simpleTabStop / tabStopWithPlaceholder
|
||||
simpleTabStop = '$' index:[0-9]+ {
|
||||
return { index: parseInt(index), content: [] };
|
||||
}
|
||||
tabStopWithPlaceholder = '${' index:[0-9]+ ':' content:placeholderContent '}' {
|
||||
return { index: parseInt(index), content: content };
|
||||
}
|
||||
placeholderContent = content:(tabStop / variable / placeholderContentText)* { return content; }
|
||||
placeholderContentText = text:placeholderContentChar+ { return text.join(''); }
|
||||
placeholderContentChar = !tabStop !variable char:[^}] { return char; }
|
||||
|
||||
variable = '${' variableContent '}' {
|
||||
return ''; // we eat variables and do nothing with them for now
|
||||
}
|
||||
variableContent = content:(variable / variableContentText)* { return content; }
|
||||
variableContentText = text:variableContentChar+ { return text.join(''); }
|
||||
variableContentChar = !variable char:[^}] { return char; }
|
||||
@@ -1,73 +0,0 @@
|
||||
Subscriber = require 'subscriber'
|
||||
_ = require 'underscore'
|
||||
|
||||
module.exports =
|
||||
class SnippetExpansion
|
||||
snippet: null
|
||||
tabStopMarkers: null
|
||||
settingTabStop: false
|
||||
|
||||
constructor: (@snippet, @editSession) ->
|
||||
|
||||
@editSession.selectToBeginningOfWord()
|
||||
startPosition = @editSession.getCursorBufferPosition()
|
||||
@editSession.transact =>
|
||||
[newRange] = @editSession.insertText(snippet.body, autoIndent: false)
|
||||
if snippet.tabStops.length > 0
|
||||
editSession.pushOperation
|
||||
do: =>
|
||||
@subscribe @editSession, 'cursor-moved.snippet-expansion', (e) => @cursorMoved(e)
|
||||
@placeTabStopMarkers(startPosition, snippet.tabStops)
|
||||
@editSession.snippetExpansion = this
|
||||
undo: => @destroy()
|
||||
@editSession.normalizeTabsInBufferRange(newRange)
|
||||
@indentSubsequentLines(startPosition.row, snippet) if snippet.lineCount > 1
|
||||
|
||||
cursorMoved: ({oldBufferPosition, newBufferPosition, bufferChanged}) ->
|
||||
return if @settingTabStop or bufferChanged
|
||||
oldTabStops = @tabStopsForBufferPosition(oldBufferPosition)
|
||||
newTabStops = @tabStopsForBufferPosition(newBufferPosition)
|
||||
@destroy() unless _.intersection(oldTabStops, newTabStops).length
|
||||
|
||||
placeTabStopMarkers: (startPosition, tabStopRanges) ->
|
||||
@tabStopMarkers = tabStopRanges.map ({start, end}) =>
|
||||
@editSession.markBufferRange([startPosition.add(start), startPosition.add(end)])
|
||||
@setTabStopIndex(0)
|
||||
|
||||
indentSubsequentLines: (startRow, snippet) ->
|
||||
initialIndent = @editSession.lineForBufferRow(startRow).match(/^\s*/)[0]
|
||||
for row in [startRow + 1...startRow + snippet.lineCount]
|
||||
@editSession.buffer.insert([row, 0], initialIndent)
|
||||
|
||||
goToNextTabStop: ->
|
||||
nextIndex = @tabStopIndex + 1
|
||||
if nextIndex < @tabStopMarkers.length
|
||||
if @setTabStopIndex(nextIndex)
|
||||
true
|
||||
else
|
||||
@goToNextTabStop()
|
||||
else
|
||||
@destroy()
|
||||
false
|
||||
|
||||
goToPreviousTabStop: ->
|
||||
@setTabStopIndex(@tabStopIndex - 1) if @tabStopIndex > 0
|
||||
|
||||
setTabStopIndex: (@tabStopIndex) ->
|
||||
@settingTabStop = true
|
||||
markerSelected = @editSession.selectMarker(@tabStopMarkers[@tabStopIndex])
|
||||
@settingTabStop = false
|
||||
markerSelected
|
||||
|
||||
tabStopsForBufferPosition: (bufferPosition) ->
|
||||
_.intersection(@tabStopMarkers, @editSession.markersForBufferPosition(bufferPosition))
|
||||
|
||||
destroy: ->
|
||||
@unsubscribe()
|
||||
marker.destroy() for marker in @tabStopMarkers
|
||||
@editSession.snippetExpansion = null
|
||||
|
||||
restore: (@editSession) ->
|
||||
@editSession.snippetExpansion = this
|
||||
|
||||
_.extend(SnippetExpansion.prototype, Subscriber)
|
||||
@@ -1,43 +0,0 @@
|
||||
_ = require 'underscore'
|
||||
Range = require 'range'
|
||||
|
||||
module.exports =
|
||||
class Snippet
|
||||
name: null
|
||||
prefix: null
|
||||
body: null
|
||||
lineCount: null
|
||||
tabStops: null
|
||||
|
||||
constructor: ({@name, @prefix, bodyTree}) ->
|
||||
@body = @extractTabStops(bodyTree)
|
||||
|
||||
extractTabStops: (bodyTree) ->
|
||||
tabStopsByIndex = {}
|
||||
bodyText = []
|
||||
[row, column] = [0, 0]
|
||||
|
||||
# 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('')
|
||||
@@ -1,131 +0,0 @@
|
||||
AtomPackage = require 'atom-package'
|
||||
fs = require 'fs'
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
_ = require 'underscore'
|
||||
SnippetExpansion = require './snippet-expansion'
|
||||
Snippet = require './snippet'
|
||||
TextMatePackage = require 'text-mate-package'
|
||||
CSON = require 'season'
|
||||
async = require 'async'
|
||||
|
||||
module.exports =
|
||||
snippetsByExtension: {}
|
||||
loaded: false
|
||||
|
||||
activate: ->
|
||||
window.snippets = this
|
||||
@loadAll()
|
||||
rootView.eachEditor (editor) =>
|
||||
@enableSnippetsInEditor(editor) if editor.attached
|
||||
|
||||
deactivate: ->
|
||||
|
||||
loadAll: ->
|
||||
packages = atom.getLoadedPackages()
|
||||
packages.push(path: config.configDirPath)
|
||||
async.eachSeries packages, @loadSnippetsFromPackage.bind(this), @doneLoading.bind(this)
|
||||
|
||||
doneLoading: ->
|
||||
@loaded = true
|
||||
|
||||
loadSnippetsFromPackage: (pack, done) ->
|
||||
if pack instanceof TextMatePackage
|
||||
@loadTextMateSnippets(pack.path, done)
|
||||
else
|
||||
@loadAtomSnippets(pack.path, done)
|
||||
|
||||
loadAtomSnippets: (packagePath, done) ->
|
||||
snippetsDirPath = path.join(packagePath, 'snippets')
|
||||
return done() unless fsUtils.isDirectorySync(snippetsDirPath)
|
||||
|
||||
loadSnippetFile = (filename, done) =>
|
||||
return done() if filename.indexOf('.') is 0
|
||||
filepath = path.join(snippetsDirPath, filename)
|
||||
CSON.readFile filepath, (err, object) =>
|
||||
if err
|
||||
console.warn "Error reading snippets file '#{filepath}': #{err.stack}"
|
||||
else
|
||||
@add(object)
|
||||
done()
|
||||
|
||||
fs.readdir snippetsDirPath, (err, paths) ->
|
||||
async.eachSeries(paths, loadSnippetFile, done)
|
||||
|
||||
loadTextMateSnippets: (bundlePath, done) ->
|
||||
snippetsDirPath = path.join(bundlePath, 'Snippets')
|
||||
return done() unless fsUtils.isDirectorySync(snippetsDirPath)
|
||||
|
||||
loadSnippetFile = (filename, done) =>
|
||||
return done() if filename.indexOf('.') is 0
|
||||
|
||||
filepath = path.join(snippetsDirPath, filename)
|
||||
|
||||
logError = (err) ->
|
||||
console.warn "Error reading snippets file '#{filepath}': #{err.stack ? err}"
|
||||
|
||||
try
|
||||
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()
|
||||
|
||||
fs.readdir snippetsDirPath, (err, paths) ->
|
||||
if err
|
||||
console.warn err
|
||||
return done()
|
||||
async.eachSeries(paths, loadSnippetFile, done)
|
||||
|
||||
translateTextmateSnippet: ({ scope, name, content, tabTrigger }) ->
|
||||
scope = syntax.cssSelectorFromScopeSelector(scope) if scope
|
||||
scope ?= '*'
|
||||
snippetsByScope = {}
|
||||
snippetsByName = {}
|
||||
snippetsByScope[scope] = snippetsByName
|
||||
snippetsByName[name] = { prefix: tabTrigger, body: content }
|
||||
snippetsByScope
|
||||
|
||||
add: (snippetsBySelector) ->
|
||||
for selector, snippetsByName of snippetsBySelector
|
||||
snippetsByPrefix = {}
|
||||
for name, attributes of snippetsByName
|
||||
{ prefix, body, bodyTree } = attributes
|
||||
# if `add` isn't called by the loader task (in specs for example), we need to parse the body
|
||||
bodyTree ?= @getBodyParser().parse(body)
|
||||
snippet = new Snippet({name, prefix, bodyTree})
|
||||
snippetsByPrefix[snippet.prefix] = snippet
|
||||
syntax.addProperties(selector, snippets: snippetsByPrefix)
|
||||
|
||||
getBodyParser: ->
|
||||
require './snippet-body-parser'
|
||||
|
||||
enableSnippetsInEditor: (editor) ->
|
||||
editor.command 'snippets:expand', (e) =>
|
||||
unless editor.getSelection().isEmpty()
|
||||
e.abortKeyBinding()
|
||||
return
|
||||
|
||||
editSession = editor.activeEditSession
|
||||
prefix = editSession.getCursor().getCurrentWordPrefix()
|
||||
if snippet = syntax.getProperty(editSession.getCursorScopes(), "snippets.#{prefix}")
|
||||
editSession.transact ->
|
||||
new SnippetExpansion(snippet, editSession)
|
||||
else
|
||||
e.abortKeyBinding()
|
||||
|
||||
editor.command 'snippets:next-tab-stop', (e) ->
|
||||
unless editor.activeEditSession.snippetExpansion?.goToNextTabStop()
|
||||
e.abortKeyBinding()
|
||||
|
||||
editor.command 'snippets:previous-tab-stop', (e) ->
|
||||
unless editor.activeEditSession.snippetExpansion?.goToPreviousTabStop()
|
||||
e.abortKeyBinding()
|
||||
@@ -1,2 +0,0 @@
|
||||
'main': './lib/snippets'
|
||||
'description': 'Expand snippets matching the current prefix with `tab`.'
|
||||
@@ -1,328 +0,0 @@
|
||||
Snippet = require 'snippets/lib/snippet'
|
||||
RootView = require 'root-view'
|
||||
Buffer = require 'text-buffer'
|
||||
Editor = require 'editor'
|
||||
_ = require 'underscore'
|
||||
Package = require 'package'
|
||||
|
||||
describe "Snippets extension", ->
|
||||
[buffer, editor, editSession] = []
|
||||
beforeEach ->
|
||||
atom.activatePackage('javascript-tmbundle', sync: true)
|
||||
window.rootView = new RootView
|
||||
rootView.open('sample.js')
|
||||
|
||||
packageWithSnippets = atom.loadPackage("package-with-snippets")
|
||||
|
||||
spyOn(require("snippets/lib/snippets"), 'loadAll')
|
||||
atom.activatePackage("snippets")
|
||||
|
||||
editor = rootView.getActiveView()
|
||||
editSession = rootView.getActivePaneItem()
|
||||
buffer = editor.getBuffer()
|
||||
rootView.simulateDomAttachment()
|
||||
rootView.enableKeymap()
|
||||
|
||||
afterEach ->
|
||||
window.snippets = null
|
||||
|
||||
describe "when 'tab' is triggered on the editor", ->
|
||||
beforeEach ->
|
||||
snippets.add
|
||||
".source.js":
|
||||
"without tab stops":
|
||||
prefix: "t1"
|
||||
body: "this is a test"
|
||||
|
||||
"tab stops":
|
||||
prefix: "t2"
|
||||
body: """
|
||||
go here next:($2) and finally go here:($0)
|
||||
go here first:($1)
|
||||
|
||||
"""
|
||||
|
||||
"indented second line":
|
||||
prefix: "t3"
|
||||
body: """
|
||||
line 1
|
||||
\tline 2$1
|
||||
|
||||
"""
|
||||
|
||||
"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}
|
||||
"""
|
||||
|
||||
"contains empty lines":
|
||||
prefix: "t7"
|
||||
body: """
|
||||
first line $1
|
||||
|
||||
|
||||
fourth line after blanks $2
|
||||
"""
|
||||
|
||||
describe "when the letters preceding the cursor trigger a snippet", ->
|
||||
describe "when the snippet contains no tab stops", ->
|
||||
it "replaces the prefix with the snippet text and places the cursor at its end", ->
|
||||
editor.insertText("t1")
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 2]
|
||||
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
expect(buffer.lineForRow(0)).toBe "this is a testvar quicksort = function () {"
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 14]
|
||||
|
||||
it "inserts a real tab the next time a tab is pressed after the snippet is expanded", ->
|
||||
editor.insertText("t1")
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
expect(buffer.lineForRow(0)).toBe "this is a testvar quicksort = function () {"
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
expect(buffer.lineForRow(0)).toBe "this is a test var quicksort = function () {"
|
||||
|
||||
describe "when the snippet contains tab stops", ->
|
||||
it "places the cursor at the first tab-stop, and moves the cursor in response to 'next-tab-stop' events", ->
|
||||
markerCountBefore = editor.activeEditSession.getMarkerCount()
|
||||
editor.setCursorScreenPosition([2, 0])
|
||||
editor.insertText('t2')
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
expect(buffer.lineForRow(2)).toBe "go here next:() and finally go here:()"
|
||||
expect(buffer.lineForRow(3)).toBe "go here first:()"
|
||||
expect(buffer.lineForRow(4)).toBe " if (items.length <= 1) return items;"
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[3, 15], [3, 15]]
|
||||
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[2, 14], [2, 14]]
|
||||
editor.insertText 'abc'
|
||||
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[2, 40], [2, 40]]
|
||||
|
||||
# tab backwards
|
||||
editor.trigger keydownEvent('tab', shiftKey: true, target: editor[0])
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[2, 14], [2, 17]] # should highlight text typed at tab stop
|
||||
|
||||
editor.trigger keydownEvent('tab', shiftKey: true, target: editor[0])
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[3, 15], [3, 15]]
|
||||
|
||||
# shift-tab on first tab-stop does nothing
|
||||
editor.trigger keydownEvent('tab', shiftKey: true, target: editor[0])
|
||||
expect(editor.getCursorScreenPosition()).toEqual [3, 15]
|
||||
|
||||
# tab through all tab stops, then tab on last stop to terminate snippet
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
expect(buffer.lineForRow(2)).toBe "go here next:(abc) and finally go here:( )"
|
||||
expect(editor.activeEditSession.getMarkerCount()).toBe markerCountBefore
|
||||
|
||||
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 tab stops are separated by blank lines", ->
|
||||
it "correctly places the tab stops (regression)", ->
|
||||
buffer.setText('')
|
||||
editor.insertText 't7'
|
||||
editor.trigger 'snippets:expand'
|
||||
editor.trigger 'snippets:next-tab-stop'
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [3, 25]
|
||||
|
||||
describe "when the cursor is moved beyond the bounds of the current tab stop", ->
|
||||
it "terminates the snippet", ->
|
||||
editor.setCursorScreenPosition([2, 0])
|
||||
editor.insertText('t2')
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
|
||||
editor.moveCursorUp()
|
||||
editor.moveCursorLeft()
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
|
||||
expect(buffer.lineForRow(2)).toBe "go here next:( ) and finally go here:()"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [2, 16]
|
||||
|
||||
# test we can terminate with shift-tab
|
||||
editor.setCursorScreenPosition([4, 0])
|
||||
editor.insertText('t2')
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
|
||||
editor.moveCursorRight()
|
||||
editor.trigger keydownEvent('tab', shiftKey: true, target: editor[0])
|
||||
expect(editor.getCursorBufferPosition()).toEqual [4, 15]
|
||||
|
||||
describe "when the snippet contains hard tabs", ->
|
||||
describe "when the edit session is in soft-tabs mode", ->
|
||||
it "translates hard tabs in the snippet to the appropriate number of spaces", ->
|
||||
expect(editSession.softTabs).toBeTruthy()
|
||||
editor.insertText("t3")
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
expect(buffer.lineForRow(1)).toBe " line 2"
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [1, 8]
|
||||
|
||||
describe "when the edit session is in hard-tabs mode", ->
|
||||
it "inserts hard tabs in the snippet directly", ->
|
||||
editSession.setSoftTabs(false)
|
||||
editor.insertText("t3")
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
expect(buffer.lineForRow(1)).toBe "\tline 2"
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [1, 7]
|
||||
|
||||
describe "when the snippet prefix is indented", ->
|
||||
describe "when the snippet spans a single line", ->
|
||||
it "does not indent the next line", ->
|
||||
editor.setCursorScreenPosition([2, Infinity])
|
||||
editor.insertText ' t1'
|
||||
editor.trigger 'snippets:expand'
|
||||
expect(buffer.lineForRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];"
|
||||
|
||||
describe "when the snippet spans multiple lines", ->
|
||||
it "indents the subsequent lines of the snippet to be even with the start of the first line", ->
|
||||
expect(editSession.softTabs).toBeTruthy()
|
||||
editor.setCursorScreenPosition([2, Infinity])
|
||||
editor.insertText ' t3'
|
||||
editor.trigger 'snippets:expand'
|
||||
expect(buffer.lineForRow(2)).toBe " if (items.length <= 1) return items; line 1"
|
||||
expect(buffer.lineForRow(3)).toBe " line 2"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [3, 12]
|
||||
|
||||
describe "when the letters preceding the cursor don't match a snippet", ->
|
||||
it "inserts a tab as normal", ->
|
||||
editor.insertText("xte")
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 3]
|
||||
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
expect(buffer.lineForRow(0)).toBe "xte var quicksort = function () {"
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 5]
|
||||
|
||||
describe "when text is selected", ->
|
||||
it "inserts a tab as normal", ->
|
||||
editor.insertText("t1")
|
||||
editor.setSelectedBufferRange([[0, 0], [0, 2]])
|
||||
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
expect(buffer.lineForRow(0)).toBe " t1var quicksort = function () {"
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 4]]
|
||||
|
||||
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 '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 "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 ' t6\n'
|
||||
editor.setCursorBufferPosition [0, Infinity]
|
||||
editor.trigger keydownEvent('tab', target: editor[0])
|
||||
expect(buffer.lineForRow(0)).toBe " first line"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 14]
|
||||
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]]
|
||||
|
||||
describe "snippet loading", ->
|
||||
beforeEach ->
|
||||
atom.loadPackage('package-with-broken-snippets.tmbundle', sync: true)
|
||||
atom.loadPackage('package-with-snippets')
|
||||
|
||||
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(snippets, 'loadAtomSnippets')
|
||||
spyOn(console, 'warn')
|
||||
snippets.loaded = false
|
||||
snippets.loadAll()
|
||||
|
||||
waitsFor "all snippets to load", 5000, -> snippets.loaded
|
||||
|
||||
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).toBe 1
|
||||
|
||||
it "loads snippets from all TextMate packages with snippets", ->
|
||||
jasmine.unspy(snippets, 'loadTextMateSnippets')
|
||||
spyOn(console, 'warn')
|
||||
snippets.loaded = false
|
||||
snippets.loadAll()
|
||||
|
||||
waitsFor "all snippets to load", 5000, -> snippets.loaded
|
||||
|
||||
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...
|
||||
}
|
||||
"""
|
||||
|
||||
# warn about invalid.plist
|
||||
expect(console.warn).toHaveBeenCalled()
|
||||
expect(console.warn.calls.length).toBe 1
|
||||
|
||||
describe "snippet body parser", ->
|
||||
it "breaks a snippet body into lines, with each line containing tab stops at the appropriate position", ->
|
||||
bodyTree = snippets.getBodyParser().parse """
|
||||
the quick brown $1fox ${2:jumped ${3:over}
|
||||
}the ${4:lazy} dog
|
||||
"""
|
||||
|
||||
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"
|
||||
]
|
||||
|
||||
it "removes interpolated variables in placeholder text (we don't currently support it)", ->
|
||||
bodyTree = snippets.getBodyParser().parse """
|
||||
module ${1:ActiveRecord::${TM_FILENAME/(?:\\A|_)([A-Za-z0-9]+)(?:\\.rb)?/(?2::\\u$1)/g}}
|
||||
"""
|
||||
|
||||
expect(bodyTree).toEqual [
|
||||
"module ",
|
||||
{
|
||||
"index": 1,
|
||||
"content": ["ActiveRecord::", ""]
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user