From 61c799e18e20c1779d732157a0e6759370c6ec71 Mon Sep 17 00:00:00 2001 From: Benjamin Gray Date: Sun, 29 Dec 2019 23:17:06 +1000 Subject: [PATCH 1/6] initial decaf --- spec/body-parser-spec.js | 271 ++++++++ spec/snippet-loading-spec.js | 303 +++++++++ spec/snippets-spec.js | 1169 ++++++++++++++++++++++++++++++++++ 3 files changed, 1743 insertions(+) create mode 100644 spec/body-parser-spec.js create mode 100644 spec/snippet-loading-spec.js create mode 100644 spec/snippets-spec.js diff --git a/spec/body-parser-spec.js b/spec/body-parser-spec.js new file mode 100644 index 0000000..475dadd --- /dev/null +++ b/spec/body-parser-spec.js @@ -0,0 +1,271 @@ +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const BodyParser = require('../lib/snippet-body-parser'); + +describe("Snippet Body Parser", function() { + it("breaks a snippet body into lines, with each line containing tab stops at the appropriate position", function() { + const bodyTree = BodyParser.parse(`\ +the quick brown $1fox \${2:jumped \${3:over} +}the \${4:lazy} dog\ +` + ); + + return 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)", function() { + const bodyTree = BodyParser.parse(`\ +module \${1:ActiveRecord::\${TM_FILENAME/(?:\\A|_)([A-Za-z0-9]+)(?:\\.rb)?/(?2::\\u$1)/g}}\ +` + ); + + return expect(bodyTree).toEqual([ + "module ", + { + "index": 1, + "content": ["ActiveRecord::", ""] + } + ]); +}); + + it("skips escaped tabstops", function() { + const bodyTree = BodyParser.parse(`\ +snippet $1 escaped \\$2 \\\\$3\ +` + ); + + return expect(bodyTree).toEqual([ + "snippet ", + { + index: 1, + content: [] + }, + " escaped $2 \\", + { + index: 3, + content: [] + } + ]); +}); + + it("includes escaped right-braces", function() { + const bodyTree = BodyParser.parse(`\ +snippet \${1:{\\}}\ +` + ); + + return expect(bodyTree).toEqual([ + "snippet ", + { + index: 1, + content: ["{}"] + } + ]); +}); + + it("parses a snippet with transformations", function() { + const bodyTree = BodyParser.parse(`\ +<\${1:p}>$0\ +` + ); + return expect(bodyTree).toEqual([ + '<', + {index: 1, content: ['p']}, + '>', + {index: 0, content: []}, + '' + ]); +}); + + it("parses a snippet with multiple tab stops with transformations", function() { + const bodyTree = BodyParser.parse(`\ +\${1:placeholder} \${1/(.)/\\u$1/} $1 \${2:ANOTHER} \${2/^(.*)$/\\L$1/} $2\ +` + ); + + return expect(bodyTree).toEqual([ + {index: 1, content: ['placeholder']}, + ' ', + { + index: 1, + content: [], + substitution: { + find: /(.)/g, + replace: [ + {escape: 'u'}, + {backreference: 1} + ] + } + }, + ' ', + {index: 1, content: []}, + ' ', + {index: 2, content: ['ANOTHER']}, + ' ', + { + index: 2, + content: [], + substitution: { + find: /^(.*)$/g, + replace: [ + {escape: 'L'}, + {backreference: 1} + ] + } + }, + ' ', + {index: 2, content: []}, + ]); +}); + + + it("parses a snippet with transformations and mirrors", function() { + const bodyTree = BodyParser.parse(`\ +\${1:placeholder}\n\${1/(.)/\\u$1/}\n$1\ +` + ); + + return expect(bodyTree).toEqual([ + {index: 1, content: ['placeholder']}, + '\n', + { + index: 1, + content: [], + substitution: { + find: /(.)/g, + replace: [ + {escape: 'u'}, + {backreference: 1} + ] + } + }, + '\n', + {index: 1, content: []} + ]); +}); + + it("parses a snippet with a format string and case-control flags", function() { + const bodyTree = BodyParser.parse(`\ +<\${1:p}>$0\ +` + ); + + return expect(bodyTree).toEqual([ + '<', + {index: 1, content: ['p']}, + '>', + {index: 0, content: []}, + '' + ]); +}); + + it("parses a snippet with an escaped forward slash in a transform", function() { + // Annoyingly, a forward slash needs to be double-backslashed just like the + // other escapes. + const bodyTree = BodyParser.parse(`\ +<\${1:p}>$0\ +` + ); + + return expect(bodyTree).toEqual([ + '<', + {index: 1, content: ['p']}, + '>', + {index: 0, content: []}, + '' + ]); +}); + + it("parses a snippet with a placeholder that mirrors another tab stop's content", function() { + const bodyTree = BodyParser.parse(`\ +$4console.\${3:log}('\${2:$1}', $1);$0\ +` + ); + + return expect(bodyTree).toEqual([ + {index: 4, content: []}, + 'console.', + {index: 3, content: ['log']}, + '(\'', + { + index: 2, content: [ + {index: 1, content: []} + ] + }, + '\', ', + {index: 1, content: []}, + ');', + {index: 0, content: []} + ]); +}); + + return it("parses a snippet with a placeholder that mixes text and tab stop references", function() { + const bodyTree = BodyParser.parse(`\ +$4console.\${3:log}('\${2:uh $1}', $1);$0\ +` + ); + + return expect(bodyTree).toEqual([ + {index: 4, content: []}, + 'console.', + {index: 3, content: ['log']}, + '(\'', + { + index: 2, content: [ + 'uh ', + {index: 1, content: []} + ] + }, + '\', ', + {index: 1, content: []}, + ');', + {index: 0, content: []} + ]); +}); +}); diff --git a/spec/snippet-loading-spec.js b/spec/snippet-loading-spec.js new file mode 100644 index 0000000..35ea4b4 --- /dev/null +++ b/spec/snippet-loading-spec.js @@ -0,0 +1,303 @@ +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const path = require('path'); +const fs = require('fs-plus'); +const temp = require('temp').track(); + +describe("Snippet Loading", function() { + let [configDirPath, snippetsService] = Array.from([]); + + beforeEach(function() { + configDirPath = temp.mkdirSync('atom-config-dir-'); + spyOn(atom, 'getConfigDirPath').andReturn(configDirPath); + + spyOn(console, 'warn'); + if (atom.notifications != null) { spyOn(atom.notifications, 'addError'); } + + return spyOn(atom.packages, 'getLoadedPackages').andReturn([ + atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-snippets')), + atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-broken-snippets')), + ]);}); + + afterEach(function() { + waitsForPromise(() => Promise.resolve(atom.packages.deactivatePackages('snippets'))); + return runs(() => jasmine.unspy(atom.packages, 'getLoadedPackages')); + }); + + const activateSnippetsPackage = function() { + waitsForPromise(() => atom.packages.activatePackage("snippets").then(function({mainModule}) { + snippetsService = mainModule.provideSnippets(); + return mainModule.loaded = false; + })); + + return waitsFor("all snippets to load", 3000, () => snippetsService.bundledSnippetsLoaded()); + }; + + it("loads the bundled snippet template snippets", function() { + activateSnippetsPackage(); + + return runs(function() { + const jsonSnippet = snippetsService.snippetsForScopes(['.source.json'])['snip']; + expect(jsonSnippet.name).toBe('Atom Snippet'); + expect(jsonSnippet.prefix).toBe('snip'); + expect(jsonSnippet.body).toContain('"prefix":'); + expect(jsonSnippet.body).toContain('"body":'); + expect(jsonSnippet.tabStopList.length).toBeGreaterThan(0); + + const csonSnippet = snippetsService.snippetsForScopes(['.source.coffee'])['snip']; + expect(csonSnippet.name).toBe('Atom Snippet'); + expect(csonSnippet.prefix).toBe('snip'); + expect(csonSnippet.body).toContain("'prefix':"); + expect(csonSnippet.body).toContain("'body':"); + return expect(csonSnippet.tabStopList.length).toBeGreaterThan(0); + }); + }); + + it("loads non-hidden snippet files from atom packages with snippets directories", function() { + activateSnippetsPackage(); + + return runs(function() { + let snippet = snippetsService.snippetsForScopes(['.test'])['test']; + expect(snippet.prefix).toBe('test'); + expect(snippet.body).toBe('testing 123'); + + snippet = snippetsService.snippetsForScopes(['.test'])['testd']; + expect(snippet.prefix).toBe('testd'); + expect(snippet.body).toBe('testing 456'); + expect(snippet.description).toBe('a description'); + expect(snippet.descriptionMoreURL).toBe('http://google.com'); + + snippet = snippetsService.snippetsForScopes(['.test'])['testlabelleft']; + expect(snippet.prefix).toBe('testlabelleft'); + expect(snippet.body).toBe('testing 456'); + expect(snippet.leftLabel).toBe('a label'); + + snippet = snippetsService.snippetsForScopes(['.test'])['testhtmllabels']; + expect(snippet.prefix).toBe('testhtmllabels'); + expect(snippet.body).toBe('testing 456'); + expect(snippet.leftLabelHTML).toBe('Label'); + return expect(snippet.rightLabelHTML).toBe('Label'); + }); + }); + + it("logs a warning if package snippets files cannot be parsed", function() { + activateSnippetsPackage(); + + return runs(function() { + // Warn about invalid-file, but don't even try to parse a hidden file + expect(console.warn.calls.length).toBeGreaterThan(0); + return expect(console.warn.mostRecentCall.args[0]).toMatch(/Error reading.*package-with-broken-snippets/); + }); + }); + + describe("::loadPackageSnippets(callback)", function() { + beforeEach(() => // simulate a list of packages where the javascript core package is returned at the end + atom.packages.getLoadedPackages.andReturn([ + atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-snippets')), + atom.packages.loadPackage('language-javascript') + ])); + + return it("allows other packages to override core packages' snippets", function() { + waitsForPromise(() => atom.packages.activatePackage("language-javascript")); + + activateSnippetsPackage(); + + return runs(function() { + const snippet = snippetsService.snippetsForScopes(['.source.js'])['log']; + return expect(snippet.body).toBe("from-a-community-package"); + }); + }); + }); + + describe("::onDidLoadSnippets(callback)", () => it("invokes listeners when all snippets are loaded", function() { + let loadedCallback = null; + + waitsFor("package to activate", done => atom.packages.activatePackage("snippets").then(function({mainModule}) { + mainModule.onDidLoadSnippets(loadedCallback = jasmine.createSpy('onDidLoadSnippets callback')); + return done(); + })); + + return waitsFor("onDidLoad callback to be called", () => loadedCallback.callCount > 0); + })); + + describe("when ~/.atom/snippets.json exists", function() { + beforeEach(function() { + fs.writeFileSync(path.join(configDirPath, 'snippets.json'), `\ +{ + ".foo": { + "foo snippet": { + "prefix": "foo", + "body": "bar1" + } + } +}\ +` + ); + return activateSnippetsPackage(); + }); + + it("loads the snippets from that file", function() { + let snippet = null; + + waitsFor(() => snippet = snippetsService.snippetsForScopes(['.foo'])['foo']); + + return runs(function() { + expect(snippet.name).toBe('foo snippet'); + expect(snippet.prefix).toBe("foo"); + return expect(snippet.body).toBe("bar1"); + }); + }); + + return describe("when that file changes", () => it("reloads the snippets", function() { + fs.writeFileSync(path.join(configDirPath, 'snippets.json'), `\ +{ +".foo": { + "foo snippet": { + "prefix": "foo", + "body": "bar2" + } +} +}\ +` + ); + + waitsFor("snippets to be changed", function() { + const snippet = snippetsService.snippetsForScopes(['.foo'])['foo']; + return (snippet != null ? snippet.body : undefined) === 'bar2'; + }); + + runs(() => fs.writeFileSync(path.join(configDirPath, 'snippets.json'), "")); + + return waitsFor("snippets to be removed", () => !snippetsService.snippetsForScopes(['.foo'])['foo']); + })); +}); + + describe("when ~/.atom/snippets.cson exists", function() { + beforeEach(function() { + fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), `\ +".foo": + "foo snippet": + "prefix": "foo" + "body": "bar1"\ +` + ); + return activateSnippetsPackage(); + }); + + it("loads the snippets from that file", function() { + let snippet = null; + + waitsFor(() => snippet = snippetsService.snippetsForScopes(['.foo'])['foo']); + + return runs(function() { + expect(snippet.name).toBe('foo snippet'); + expect(snippet.prefix).toBe("foo"); + return expect(snippet.body).toBe("bar1"); + }); + }); + + return describe("when that file changes", () => it("reloads the snippets", function() { + fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), `\ +".foo": +"foo snippet": + "prefix": "foo" + "body": "bar2"\ +` + ); + + waitsFor("snippets to be changed", function() { + const snippet = snippetsService.snippetsForScopes(['.foo'])['foo']; + return (snippet != null ? snippet.body : undefined) === 'bar2'; + }); + + runs(() => fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), "")); + + return waitsFor("snippets to be removed", function() { + const snippet = snippetsService.snippetsForScopes(['.foo'])['foo']; + return (snippet == null); + }); + })); + }); + + it("notifies the user when the user snippets file cannot be loaded", function() { + fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), `\ +".junk":::\ +` + ); + + activateSnippetsPackage(); + + return runs(function() { + expect(console.warn).toHaveBeenCalled(); + if (atom.notifications != null) { return expect(atom.notifications.addError).toHaveBeenCalled(); } + }); + }); + + return describe("packages-with-snippets-disabled feature", function() { + it("disables no snippets if the config option is empty", function() { + const originalConfig = atom.config.get('core.packagesWithSnippetsDisabled'); + atom.config.set('core.packagesWithSnippetsDisabled', []); + + activateSnippetsPackage(); + return runs(function() { + const snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']); + expect(Object.keys(snippets).length).toBe(1); + return atom.config.set('core.packagesWithSnippetsDisabled', originalConfig); + }); + }); + + it("still includes a disabled package's snippets in the list of unparsed snippets", function() { + let originalConfig = atom.config.get('core.packagesWithSnippetsDisabled'); + atom.config.set('core.packagesWithSnippetsDisabled', []); + + activateSnippetsPackage(); + return runs(function() { + atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets']); + const allSnippets = snippetsService.getUnparsedSnippets(); + const scopedSnippet = allSnippets.find(s => s.selectorString === '.package-with-snippets-unique-scope'); + expect(scopedSnippet).not.toBe(undefined); + return originalConfig = atom.config.get('core.packagesWithSnippetsDisabled'); + }); + }); + + it("never loads a package's snippets when that package is disabled in config", function() { + const originalConfig = atom.config.get('core.packagesWithSnippetsDisabled'); + atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets']); + + activateSnippetsPackage(); + return runs(function() { + const snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']); + expect(Object.keys(snippets).length).toBe(0); + return atom.config.set('core.packagesWithSnippetsDisabled', originalConfig); + }); + }); + + return it("unloads and/or reloads snippets from a package if the config option is changed after activation", function() { + const originalConfig = atom.config.get('core.packagesWithSnippetsDisabled'); + atom.config.set('core.packagesWithSnippetsDisabled', []); + + activateSnippetsPackage(); + return runs(function() { + let snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']); + expect(Object.keys(snippets).length).toBe(1); + + // Disable it. + atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets']); + snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']); + expect(Object.keys(snippets).length).toBe(0); + + // Re-enable it. + atom.config.set('core.packagesWithSnippetsDisabled', []); + snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']); + expect(Object.keys(snippets).length).toBe(1); + + return atom.config.set('core.packagesWithSnippetsDisabled', originalConfig); + }); + }); + }); +}); diff --git a/spec/snippets-spec.js b/spec/snippets-spec.js new file mode 100644 index 0000000..8ce39e1 --- /dev/null +++ b/spec/snippets-spec.js @@ -0,0 +1,1169 @@ +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const path = require('path'); +const temp = require('temp').track(); +const Snippets = require('../lib/snippets'); +const {TextEditor} = require('atom'); + +describe("Snippets extension", function() { + let [editorElement, editor] = Array.from([]); + + const simulateTabKeyEvent = function(param) { + if (param == null) { param = {}; } + const {shift} = param; + const event = atom.keymaps.constructor.buildKeydownEvent('tab', {shift, target: editorElement}); + return atom.keymaps.handleKeyboardEvent(event); + }; + + beforeEach(function() { + spyOn(Snippets, 'loadAll'); + spyOn(Snippets, 'getUserSnippetsPath').andReturn(''); + + waitsForPromise(() => atom.workspace.open('sample.js')); + + waitsForPromise(() => atom.packages.activatePackage('language-javascript')); + + waitsForPromise(() => atom.packages.activatePackage('snippets')); + + return runs(function() { + editor = atom.workspace.getActiveTextEditor(); + return editorElement = atom.views.getView(editor); + }); + }); + + afterEach(() => waitsForPromise(() => atom.packages.deactivatePackage('snippets'))); + + describe("provideSnippets interface", function() { + let snippetsInterface = null; + + beforeEach(() => snippetsInterface = Snippets.provideSnippets()); + + describe("bundledSnippetsLoaded", function() { + it("indicates the loaded state of the bundled snippets", function() { + expect(snippetsInterface.bundledSnippetsLoaded()).toBe(false); + Snippets.doneLoading(); + return expect(snippetsInterface.bundledSnippetsLoaded()).toBe(true); + }); + + return it("resets the loaded state after snippets is deactivated", function() { + expect(snippetsInterface.bundledSnippetsLoaded()).toBe(false); + Snippets.doneLoading(); + expect(snippetsInterface.bundledSnippetsLoaded()).toBe(true); + + waitsForPromise(() => atom.packages.deactivatePackage('snippets')); + waitsForPromise(() => atom.packages.activatePackage('snippets')); + + return runs(function() { + expect(snippetsInterface.bundledSnippetsLoaded()).toBe(false); + Snippets.doneLoading(); + return expect(snippetsInterface.bundledSnippetsLoaded()).toBe(true); + }); + }); + }); + + return describe("insertSnippet", () => it("can insert a snippet", function() { + editor.setSelectedBufferRange([[0, 4], [0, 13]]); + snippetsInterface.insertSnippet("hello ${1:world}", editor); + return expect(editor.lineTextForBufferRow(0)).toBe("var hello world = function () {"); + })); + }); + + it("returns false for snippetToExpandUnderCursor if getSnippets returns {}", function() { + const snippets = atom.packages.getActivePackage('snippets').mainModule; + return expect(snippets.snippetToExpandUnderCursor(editor)).toEqual(false); + }); + + it("ignores invalid snippets in the config", function() { + const snippets = atom.packages.getActivePackage('snippets').mainModule; + + let invalidSnippets = null; + spyOn(snippets.scopedPropertyStore, 'getPropertyValue').andCallFake(() => invalidSnippets); + expect(snippets.getSnippets(editor)).toEqual({}); + + invalidSnippets = 'test'; + expect(snippets.getSnippets(editor)).toEqual({}); + + invalidSnippets = []; + expect(snippets.getSnippets(editor)).toEqual({}); + + invalidSnippets = 3; + expect(snippets.getSnippets(editor)).toEqual({}); + + invalidSnippets = {a: null}; + return expect(snippets.getSnippets(editor)).toEqual({}); +}); + + describe("when null snippets are present", function() { + beforeEach(() => Snippets.add(__filename, { + '.source.js': { + "some snippet": { + prefix: "t1", + body: "this is a test" + } + }, + + '.source.js .nope': { + "some snippet": { + prefix: "t1", + body: null + } + } + } + )); + + return it("overrides the less-specific defined snippet", function() { + const snippets = Snippets.provideSnippets(); + expect(snippets.snippetsForScopes(['.source.js'])['t1']).toBeTruthy(); + return expect(snippets.snippetsForScopes(['.source.js .nope.not-today'])['t1']).toBeFalsy(); + }); + }); + + describe("when 'tab' is triggered on the editor", function() { + beforeEach(() => Snippets.add(__filename, { + ".source.js": { + "without tab stops": { + prefix: "t1", + body: "this is a test" + }, + + "with only an end tab stop": { + prefix: "t1a", + body: "something $0 strange" + }, + + "overlapping prefix": { + prefix: "tt1", + body: "this is another test" + }, + + "special chars": { + prefix: "@unique", + body: "@unique see" + }, + + "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 +$2\ +` + }, + + "multiline with indented placeholder tabstop": { + prefix: "t4", + body: `\ +line \${1:1} +\${2:body...}\ +` + }, + + "multiline starting with tabstop": { + prefix: "t4b", + body: `\ +$1 = line 1 { +line 2 +}\ +` + }, + + "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}\ +` + }, + + "tab stops at beginning and then end of snippet": { + prefix: "t6b", + body: "$1expanded$0" + }, + + "tab stops at end and then beginning of snippet": { + prefix: "t6c", + body: "$0expanded$1" + }, + + "contains empty lines": { + prefix: "t7", + body: `\ +first line $1 + + +fourth line after blanks $2\ +` + }, + "with/without placeholder": { + prefix: "t8", + body: `\ +with placeholder \${1:test} +without placeholder \${2}\ +` + }, + + "multi-caret": { + prefix: "t9", + body: `\ +with placeholder \${1:test} +without placeholder $1\ +` + }, + + "multi-caret-multi-tabstop": { + prefix: "t9b", + body: `\ +with placeholder \${1:test} +without placeholder $1 +second tabstop $2 +third tabstop $3\ +` + }, + + "large indices": { + prefix: "t10", + body: `\ +hello\${10} \${11:large} indices\${1}\ +` + }, + + "no body": { + prefix: "bad1" + }, + + "number body": { + prefix: "bad2", + body: 100 + }, + + "many tabstops": { + prefix: "t11", + body: `\ +$0one\${1} \${2:two} three\${3}\ +` + }, + + "simple transform": { + prefix: "t12", + body: `\ +[\${1:b}][/\${1/[ ]+.*$//}]\ +` + }, + "transform with non-transforming mirrors": { + prefix: "t13", + body: `\ +\${1:placeholder}\n\${1/(.)/\\u$1/}\n$1\ +` + }, + "multiple tab stops, some with transforms and some without": { + prefix: "t14", + body: `\ +\${1:placeholder} \${1/(.)/\\u$1/} $1 \${2:ANOTHER} \${2/^(.*)$/\\L$1/} $2\ +` + }, + "has a transformed tab stop without a corresponding ordinary tab stop": { + prefix: 't15', + body: `\ +\${1/(.)/\\u$1/} & $2\ +` + }, + "has a transformed tab stop that occurs before the corresponding ordinary tab stop": { + prefix: 't16', + body: `\ +& \${1/(.)/\\u$1/} & \${1:q}\ +` + }, + "has a placeholder that mirrors another tab stop's content": { + prefix: 't17', + body: "$4console.${3:log}('${2:uh $1}', $1);$0" + }, + "has a transformed tab stop such that it is possible to move the cursor between the ordinary tab stop and its transformed version without an intermediate step": { + prefix: 't18', + body: '// $1\n// ${1/./=/}' + } + } + } + )); + + it("parses snippets once, reusing cached ones on subsequent queries", function() { + spyOn(Snippets, "getBodyParser").andCallThrough(); + + editor.insertText("t1"); + simulateTabKeyEvent(); + + expect(Snippets.getBodyParser).toHaveBeenCalled(); + expect(editor.lineTextForBufferRow(0)).toBe("this is a testvar quicksort = function () {"); + expect(editor.getCursorScreenPosition()).toEqual([0, 14]); + + Snippets.getBodyParser.reset(); + + editor.setText(""); + editor.insertText("t1"); + simulateTabKeyEvent(); + + expect(Snippets.getBodyParser).not.toHaveBeenCalled(); + expect(editor.lineTextForBufferRow(0)).toBe("this is a test"); + expect(editor.getCursorScreenPosition()).toEqual([0, 14]); + + Snippets.getBodyParser.reset(); + + Snippets.add(__filename, { + ".source.js": { + "invalidate previous snippet": { + prefix: "t1", + body: "new snippet" + } + } + } + ); + + editor.setText(""); + editor.insertText("t1"); + simulateTabKeyEvent(); + + expect(Snippets.getBodyParser).toHaveBeenCalled(); + expect(editor.lineTextForBufferRow(0)).toBe("new snippet"); + return expect(editor.getCursorScreenPosition()).toEqual([0, 11]); + }); + + describe("when the snippet body is invalid or missing", () => it("does not register the snippet", function() { + editor.setText(''); + editor.insertText('bad1'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + expect(editor.getText()).toBe('bad1'); + + editor.setText(''); + editor.setText('bad2'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + return expect(editor.getText()).toBe('bad2'); + })); + + describe("when the letters preceding the cursor trigger a snippet", function() { + describe("when the snippet contains no tab stops", function() { + it("replaces the prefix with the snippet text and places the cursor at its end", function() { + editor.insertText("t1"); + expect(editor.getCursorScreenPosition()).toEqual([0, 2]); + + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("this is a testvar quicksort = function () {"); + return expect(editor.getCursorScreenPosition()).toEqual([0, 14]); + }); + + return it("inserts a real tab the next time a tab is pressed after the snippet is expanded", function() { + editor.insertText("t1"); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("this is a testvar quicksort = function () {"); + simulateTabKeyEvent(); + return expect(editor.lineTextForBufferRow(0)).toBe("this is a test var quicksort = function () {"); + }); + }); + + describe("when the snippet contains tab stops", function() { + it("places the cursor at the first tab-stop, and moves the cursor in response to 'next-tab-stop' events", function() { + const markerCountBefore = editor.getMarkerCount(); + editor.setCursorScreenPosition([2, 0]); + editor.insertText('t2'); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(2)).toBe("go here next:() and finally go here:()"); + expect(editor.lineTextForBufferRow(3)).toBe("go here first:()"); + expect(editor.lineTextForBufferRow(4)).toBe(" if (items.length <= 1) return items;"); + expect(editor.getSelectedBufferRange()).toEqual([[3, 15], [3, 15]]); + + simulateTabKeyEvent(); + expect(editor.getSelectedBufferRange()).toEqual([[2, 14], [2, 14]]); + editor.insertText('abc'); + + simulateTabKeyEvent(); + expect(editor.getSelectedBufferRange()).toEqual([[2, 40], [2, 40]]); + + // tab backwards + simulateTabKeyEvent({shift: true}); + expect(editor.getSelectedBufferRange()).toEqual([[2, 14], [2, 17]]); // should highlight text typed at tab stop + + simulateTabKeyEvent({shift: true}); + expect(editor.getSelectedBufferRange()).toEqual([[3, 15], [3, 15]]); + + // shift-tab on first tab-stop does nothing + simulateTabKeyEvent({shift: true}); + expect(editor.getCursorScreenPosition()).toEqual([3, 15]); + + // tab through all tab stops, then tab on last stop to terminate snippet + simulateTabKeyEvent(); + simulateTabKeyEvent(); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(2)).toBe("go here next:(abc) and finally go here:( )"); + return expect(editor.getMarkerCount()).toBe(markerCountBefore); + }); + + describe("when tab stops are nested", () => it("destroys the inner tab stop if the outer tab stop is modified", function() { + editor.setText(''); + editor.insertText('t5'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + expect(editor.lineTextForBufferRow(0)).toBe('"key": value'); + expect(editor.getSelectedBufferRange()).toEqual([[0, 0], [0, 5]]); + editor.insertText("foo"); + simulateTabKeyEvent(); + return expect(editor.getSelectedBufferRange()).toEqual([[0, 5], [0, 10]]); + })); + + describe("when the only tab stop is an end stop", () => it("terminates the snippet immediately after moving the cursor to the end stop", function() { + editor.setText(''); + editor.insertText('t1a'); + simulateTabKeyEvent(); + + expect(editor.lineTextForBufferRow(0)).toBe("something strange"); + expect(editor.getCursorBufferPosition()).toEqual([0, 10]); + + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("something strange"); + return expect(editor.getCursorBufferPosition()).toEqual([0, 12]); + })); + + describe("when tab stops are separated by blank lines", () => it("correctly places the tab stops (regression)", function() { + editor.setText(''); + editor.insertText('t7'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + atom.commands.dispatch(editorElement, 'snippets:next-tab-stop'); + return expect(editor.getCursorBufferPosition()).toEqual([3, 25]); + })); + + describe("when the cursor is moved beyond the bounds of the current tab stop", () => it("terminates the snippet", function() { + editor.setCursorScreenPosition([2, 0]); + editor.insertText('t2'); + simulateTabKeyEvent(); + + editor.moveUp(); + editor.moveLeft(); + simulateTabKeyEvent(); + + expect(editor.lineTextForBufferRow(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'); + simulateTabKeyEvent(); + simulateTabKeyEvent(); + + editor.moveRight(); + simulateTabKeyEvent({shift: true}); + return expect(editor.getCursorBufferPosition()).toEqual([4, 15]); + })); + + describe("when the cursor is moved within the bounds of the current tab stop", () => it("should not terminate the snippet", function() { + editor.setCursorScreenPosition([0, 0]); + editor.insertText('t8'); + simulateTabKeyEvent(); + + expect(editor.lineTextForBufferRow(0)).toBe("with placeholder test"); + editor.moveRight(); + editor.moveLeft(); + editor.insertText("foo"); + expect(editor.lineTextForBufferRow(0)).toBe("with placeholder tesfoot"); + + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder var quicksort = function () {"); + editor.insertText("test"); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder testvar quicksort = function () {"); + editor.moveLeft(); + editor.insertText("foo"); + return expect(editor.lineTextForBufferRow(1)).toBe("without placeholder tesfootvar quicksort = function () {"); + })); + + return describe("when the backspace is press within the bounds of the current tab stop", () => it("should not terminate the snippet", function() { + editor.setCursorScreenPosition([0, 0]); + editor.insertText('t8'); + simulateTabKeyEvent(); + + expect(editor.lineTextForBufferRow(0)).toBe("with placeholder test"); + editor.moveRight(); + editor.backspace(); + editor.insertText("foo"); + expect(editor.lineTextForBufferRow(0)).toBe("with placeholder tesfoo"); + + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder var quicksort = function () {"); + editor.insertText("test"); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder testvar quicksort = function () {"); + editor.backspace(); + editor.insertText("foo"); + return expect(editor.lineTextForBufferRow(1)).toBe("without placeholder tesfoovar quicksort = function () {"); + })); + }); + + describe("when the snippet contains hard tabs", function() { + describe("when the edit session is in soft-tabs mode", () => it("translates hard tabs in the snippet to the appropriate number of spaces", function() { + expect(editor.getSoftTabs()).toBeTruthy(); + editor.insertText("t3"); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(1)).toBe(" line 2"); + return expect(editor.getCursorBufferPosition()).toEqual([1, 8]); + })); + + return describe("when the edit session is in hard-tabs mode", () => it("inserts hard tabs in the snippet directly", function() { + editor.setSoftTabs(false); + editor.insertText("t3"); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(1)).toBe("\tline 2"); + return expect(editor.getCursorBufferPosition()).toEqual([1, 7]); + })); + }); + + describe("when the snippet prefix is indented", function() { + describe("when the snippet spans a single line", () => it("does not indent the next line", function() { + editor.setCursorScreenPosition([2, Infinity]); + editor.insertText(' t1'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + return expect(editor.lineTextForBufferRow(3)).toBe(" var pivot = items.shift(), current, left = [], right = [];"); + })); + + return 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", function() { + expect(editor.getSoftTabs()).toBeTruthy(); + editor.setCursorScreenPosition([2, Infinity]); + editor.insertText(' t3'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + expect(editor.lineTextForBufferRow(2)).toBe(" if (items.length <= 1) return items; line 1"); + expect(editor.lineTextForBufferRow(3)).toBe(" line 2"); + return expect(editor.getCursorBufferPosition()).toEqual([3, 12]); + })); + }); + + describe("when the snippet spans multiple lines", function() { + beforeEach(function() { + editor.update({autoIndent: true}); + // editor.update() returns a Promise that never gets resolved, so we + // need to return undefined to avoid a timeout in the spec. + // TODO: Figure out why `editor.update({autoIndent: true})` never gets resolved. + }); + + it("places tab stops correctly", function() { + expect(editor.getSoftTabs()).toBeTruthy(); + editor.setCursorScreenPosition([2, Infinity]); + editor.insertText(' t3'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + expect(editor.getCursorBufferPosition()).toEqual([3, 12]); + atom.commands.dispatch(editorElement, 'snippets:next-tab-stop'); + return expect(editor.getCursorBufferPosition()).toEqual([4, 4]); + }); + + it("indents the subsequent lines of the snippet based on the indent level before the snippet is inserted", function() { + editor.setCursorScreenPosition([2, Infinity]); + editor.insertNewline(); + editor.insertText('t4b'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + + expect(editor.lineTextForBufferRow(3)).toBe(" = line 1 {"); // 4 + 1 spaces (because the tab stop is invisible) + expect(editor.lineTextForBufferRow(4)).toBe(" line 2"); + expect(editor.lineTextForBufferRow(5)).toBe(" }"); + return expect(editor.getCursorBufferPosition()).toEqual([3, 4]); + }); + + return it("does not change the relative positioning of the tab stops when inserted multiple times", function() { + editor.setCursorScreenPosition([2, Infinity]); + editor.insertNewline(); + editor.insertText('t4'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + + expect(editor.getSelectedBufferRange()).toEqual([[3, 9], [3, 10]]); + atom.commands.dispatch(editorElement, 'snippets:next-tab-stop'); + expect(editor.getSelectedBufferRange()).toEqual([[4, 6], [4, 13]]); + + editor.insertText('t4'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + + expect(editor.getSelectedBufferRange()).toEqual([[4, 11], [4, 12]]); + atom.commands.dispatch(editorElement, 'snippets:next-tab-stop'); + expect(editor.getSelectedBufferRange()).toEqual([[5, 8], [5, 15]]); + + editor.setText(''); // Clear editor + editor.insertText('t4'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + + expect(editor.getSelectedBufferRange()).toEqual([[0, 5], [0, 6]]); + atom.commands.dispatch(editorElement, 'snippets:next-tab-stop'); + return expect(editor.getSelectedBufferRange()).toEqual([[1, 2], [1, 9]]); + }); + }); + + return describe("when multiple snippets match the prefix", () => it("expands the snippet that is the longest match for the prefix", function() { + editor.insertText('t113'); + expect(editor.getCursorScreenPosition()).toEqual([0, 4]); + + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("t113 var quicksort = function () {"); + expect(editor.getCursorScreenPosition()).toEqual([0, 6]); + + editor.undo(); + editor.undo(); + + editor.insertText("tt1"); + expect(editor.getCursorScreenPosition()).toEqual([0, 3]); + + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("this is another testvar quicksort = function () {"); + expect(editor.getCursorScreenPosition()).toEqual([0, 20]); + + editor.undo(); + editor.undo(); + + editor.insertText("@t1"); + expect(editor.getCursorScreenPosition()).toEqual([0, 3]); + + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("@this is a testvar quicksort = function () {"); + return expect(editor.getCursorScreenPosition()).toEqual([0, 15]); + })); + }); + + describe("when the word preceding the cursor ends with a snippet prefix", () => it("inserts a tab as normal", function() { + editor.insertText("t1t1t1"); + simulateTabKeyEvent(); + return expect(editor.lineTextForBufferRow(0)).toBe("t1t1t1 var quicksort = function () {"); + })); + + describe("when the letters preceding the cursor don't match a snippet", () => it("inserts a tab as normal", function() { + editor.insertText("xxte"); + expect(editor.getCursorScreenPosition()).toEqual([0, 4]); + + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("xxte var quicksort = function () {"); + return expect(editor.getCursorScreenPosition()).toEqual([0, 6]); + })); + + describe("when text is selected", () => it("inserts a tab as normal", function() { + editor.insertText("t1"); + editor.setSelectedBufferRange([[0, 0], [0, 2]]); + + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe(" t1var quicksort = function () {"); + return expect(editor.getSelectedBufferRange()).toEqual([[0, 0], [0, 4]]); + })); + + describe("when a previous snippet expansion has just been undone", function() { + describe("when the tab stops appear in the middle of the snippet", () => it("expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", function() { + editor.insertText('t6\n'); + editor.setCursorBufferPosition([0, 2]); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("first line"); + editor.undo(); + expect(editor.lineTextForBufferRow(0)).toBe("t6"); + simulateTabKeyEvent(); + return expect(editor.lineTextForBufferRow(0)).toBe("first line"); + })); + + describe("when the tab stops appear at the beginning and then the end of snippet", () => it("expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", function() { + editor.insertText('t6b\n'); + editor.setCursorBufferPosition([0, 3]); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("expanded"); + editor.undo(); + expect(editor.lineTextForBufferRow(0)).toBe("t6b"); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("expanded"); + return expect(editor.getCursorBufferPosition()).toEqual([0, 0]); + })); + + return describe("when the tab stops appear at the end and then the beginning of snippet", () => it("expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", function() { + editor.insertText('t6c\n'); + editor.setCursorBufferPosition([0, 3]); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("expanded"); + editor.undo(); + expect(editor.lineTextForBufferRow(0)).toBe("t6c"); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("expanded"); + return expect(editor.getCursorBufferPosition()).toEqual([0, 8]); + })); + }); + + describe("when the prefix contains non-word characters", function() { + it("selects the non-word characters as part of the prefix", function() { + editor.insertText("@unique"); + expect(editor.getCursorScreenPosition()).toEqual([0, 7]); + + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("@unique seevar quicksort = function () {"); + expect(editor.getCursorScreenPosition()).toEqual([0, 11]); + + editor.setCursorBufferPosition([10, 0]); + editor.insertText("'@unique"); + + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(10)).toBe("'@unique see"); + return expect(editor.getCursorScreenPosition()).toEqual([10, 12]); + }); + + return it("does not select the whitespace before the prefix", function() { + editor.insertText("a; @unique"); + expect(editor.getCursorScreenPosition()).toEqual([0, 10]); + + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("a; @unique seevar quicksort = function () {"); + return expect(editor.getCursorScreenPosition()).toEqual([0, 14]); + }); + }); + + describe("when snippet contains tabstops with or without placeholder", () => it("should create two markers", function() { + editor.setCursorScreenPosition([0, 0]); + editor.insertText('t8'); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("with placeholder test"); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder var quicksort = function () {"); + + expect(editor.getSelectedBufferRange()).toEqual([[0, 17], [0, 21]]); + + simulateTabKeyEvent(); + return expect(editor.getSelectedBufferRange()).toEqual([[1, 20], [1, 20]]); + })); + + describe("when snippet contains multi-caret tabstops with or without placeholder", function() { + it("should create two markers", function() { + editor.setCursorScreenPosition([0, 0]); + editor.insertText('t9'); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("with placeholder test"); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder var quicksort = function () {"); + editor.insertText('hello'); + expect(editor.lineTextForBufferRow(0)).toBe("with placeholder hello"); + return expect(editor.lineTextForBufferRow(1)).toBe("without placeholder hellovar quicksort = function () {"); + }); + + it("terminates the snippet when cursors are destroyed", function() { + editor.setCursorScreenPosition([0, 0]); + editor.insertText('t9b'); + simulateTabKeyEvent(); + editor.getCursors()[0].destroy(); + editor.getCursorBufferPosition(); + simulateTabKeyEvent(); + + return expect(editor.lineTextForBufferRow(1)).toEqual("without placeholder "); + }); + + it("terminates the snippet expansion if a new cursor moves outside the bounds of the tab stops", function() { + editor.setCursorScreenPosition([0, 0]); + editor.insertText('t9b'); + simulateTabKeyEvent(); + editor.insertText('test'); + + editor.getCursors()[0].destroy(); + editor.moveDown(); // this should destroy the previous expansion + editor.moveToBeginningOfLine(); + + // this should insert whitespace instead of going through tabstops of the previous destroyed snippet + simulateTabKeyEvent(); + return expect(editor.lineTextForBufferRow(2).indexOf(" second")).toBe(0); + }); + + it("moves to the second tabstop after a multi-caret tabstop", function() { + editor.setCursorScreenPosition([0, 0]); + editor.insertText('t9b'); + simulateTabKeyEvent(); + editor.insertText('line 1'); + + simulateTabKeyEvent(); + editor.insertText('line 2'); + + simulateTabKeyEvent(); + editor.insertText('line 3'); + + return expect(editor.lineTextForBufferRow(2).indexOf("line 2 ")).toBe(-1); + }); + + return it("mirrors input properly when a tabstop's placeholder refers to another tabstop", function() { + editor.setText('t17'); + editor.setCursorScreenPosition([0, 3]); + simulateTabKeyEvent(); + editor.insertText("foo"); + expect(editor.getText()).toBe("console.log('uh foo', foo);"); + simulateTabKeyEvent(); + editor.insertText("bar"); + return expect(editor.getText()).toBe("console.log('bar', foo);"); + }); + }); + + describe("when the snippet contains tab stops with transformations", function() { + it("transforms the text typed into the first tab stop before setting it in the transformed tab stop", function() { + editor.setText('t12'); + editor.setCursorScreenPosition([0, 3]); + simulateTabKeyEvent(); + expect(editor.getText()).toBe("[b][/b]"); + editor.insertText('img src'); + return expect(editor.getText()).toBe("[img src][/img]"); + }); + + it("bundles the transform mutations along with the original manual mutation for the purposes of undo and redo", function() { + editor.setText('t12'); + editor.setCursorScreenPosition([0, 3]); + simulateTabKeyEvent(); + editor.insertText('i'); + expect(editor.getText()).toBe("[i][/i]"); + + editor.insertText('mg src'); + expect(editor.getText()).toBe("[img src][/img]"); + + editor.undo(); + expect(editor.getText()).toBe("[i][/i]"); + + editor.redo(); + return expect(editor.getText()).toBe("[img src][/img]"); + }); + + it("can pick the right insertion to use as the primary even if a transformed insertion occurs first in the snippet", function() { + editor.setText('t16'); + editor.setCursorScreenPosition([0, 3]); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("& Q & q"); + expect(editor.getCursorBufferPosition()).toEqual([0, 7]); + + editor.insertText('rst'); + return expect(editor.lineTextForBufferRow(0)).toBe("& RST & rst"); + }); + + return it("silently ignores a tab stop without a non-transformed insertion to use as the primary", function() { + editor.setText('t15'); + editor.setCursorScreenPosition([0, 3]); + simulateTabKeyEvent(); + editor.insertText('a'); + expect(editor.lineTextForBufferRow(0)).toBe(" & a"); + return expect(editor.getCursorBufferPosition()).toEqual([0, 4]); + }); + }); + + describe("when the snippet contains mirrored tab stops and tab stops with transformations", () => it("adds cursors for the mirrors but not the transformations", function() { + editor.setText('t13'); + editor.setCursorScreenPosition([0, 3]); + simulateTabKeyEvent(); + expect(editor.getCursors().length).toBe(2); + expect(editor.getText()).toBe(`\ +placeholder +PLACEHOLDER +\ +` + ); + + editor.insertText('foo'); + + return expect(editor.getText()).toBe(`\ +foo +FOO +foo\ +` + ); + })); + + describe("when the snippet contains multiple tab stops, some with transformations and some without", () => it("does not get confused", function() { + editor.setText('t14'); + editor.setCursorScreenPosition([0, 3]); + simulateTabKeyEvent(); + expect(editor.getCursors().length).toBe(2); + expect(editor.getText()).toBe("placeholder PLACEHOLDER ANOTHER another "); + simulateTabKeyEvent(); + expect(editor.getCursors().length).toBe(2); + editor.insertText('FOO'); + return expect(editor.getText()).toBe(`\ +placeholder PLACEHOLDER FOO foo FOO\ +` + ); + })); + + describe("when the snippet has a transformed tab stop such that it is possible to move the cursor between the ordinary tab stop and its transformed version without an intermediate step", () => it("terminates the snippet upon such a cursor move", function() { + editor.setText('t18'); + editor.setCursorScreenPosition([0, 3]); + simulateTabKeyEvent(); + expect(editor.getText()).toBe("// \n// "); + expect(editor.getCursorBufferPosition()).toEqual([0, 3]); + editor.insertText('wat'); + expect(editor.getText()).toBe("// wat\n// ==="); + // Move the cursor down one line, then up one line. This puts the cursor + // back in its previous position, but the snippet should no longer be + // active, so when we type more text, it should not be mirrored. + editor.setCursorScreenPosition([1, 6]); + editor.setCursorScreenPosition([0, 6]); + editor.insertText('wat'); + return expect(editor.getText()).toBe("// watwat\n// ==="); + })); + + + describe("when the snippet contains tab stops with an index >= 10", () => it("parses and orders the indices correctly", function() { + editor.setText('t10'); + editor.setCursorScreenPosition([0, 3]); + simulateTabKeyEvent(); + expect(editor.getText()).toBe("hello large indices"); + expect(editor.getCursorBufferPosition()).toEqual([0, 19]); + simulateTabKeyEvent(); + expect(editor.getCursorBufferPosition()).toEqual([0, 5]); + simulateTabKeyEvent(); + return expect(editor.getSelectedBufferRange()).toEqual([[0, 6], [0, 11]]); + })); + + describe("when there are multiple cursors", function() { + describe("when the cursors share a common snippet prefix", function() { + it("expands the snippet for all cursors and allows simultaneous editing", function() { + editor.insertText('t9'); + editor.setCursorBufferPosition([12, 2]); + editor.insertText(' t9'); + editor.addCursorAtBufferPosition([0, 2]); + simulateTabKeyEvent(); + + expect(editor.lineTextForBufferRow(0)).toBe("with placeholder test"); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder var quicksort = function () {"); + expect(editor.lineTextForBufferRow(13)).toBe("}; with placeholder test"); + expect(editor.lineTextForBufferRow(14)).toBe("without placeholder "); + + editor.insertText('hello'); + expect(editor.lineTextForBufferRow(0)).toBe("with placeholder hello"); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder hellovar quicksort = function () {"); + expect(editor.lineTextForBufferRow(13)).toBe("}; with placeholder hello"); + return expect(editor.lineTextForBufferRow(14)).toBe("without placeholder hello"); + }); + + it("applies transformations identically to single-expansion mode", function() { + editor.setText('t14\nt14'); + editor.setCursorBufferPosition([1, 3]); + editor.addCursorAtBufferPosition([0, 3]); + simulateTabKeyEvent(); + + expect(editor.lineTextForBufferRow(0)).toBe("placeholder PLACEHOLDER ANOTHER another "); + expect(editor.lineTextForBufferRow(1)).toBe("placeholder PLACEHOLDER ANOTHER another "); + + editor.insertText("testing"); + + expect(editor.lineTextForBufferRow(0)).toBe("testing TESTING testing ANOTHER another "); + expect(editor.lineTextForBufferRow(1)).toBe("testing TESTING testing ANOTHER another "); + + simulateTabKeyEvent(); + editor.insertText("AGAIN"); + + expect(editor.lineTextForBufferRow(0)).toBe("testing TESTING testing AGAIN again AGAIN"); + return expect(editor.lineTextForBufferRow(1)).toBe("testing TESTING testing AGAIN again AGAIN"); + }); + + it("bundles transform-induced mutations into a single history entry along with their triggering edit, even across multiple snippets", function() { + editor.setText('t14\nt14'); + editor.setCursorBufferPosition([1, 3]); + editor.addCursorAtBufferPosition([0, 3]); + simulateTabKeyEvent(); + + expect(editor.lineTextForBufferRow(0)).toBe("placeholder PLACEHOLDER ANOTHER another "); + expect(editor.lineTextForBufferRow(1)).toBe("placeholder PLACEHOLDER ANOTHER another "); + + editor.insertText("testing"); + + expect(editor.lineTextForBufferRow(0)).toBe("testing TESTING testing ANOTHER another "); + expect(editor.lineTextForBufferRow(1)).toBe("testing TESTING testing ANOTHER another "); + + simulateTabKeyEvent(); + editor.insertText("AGAIN"); + + expect(editor.lineTextForBufferRow(0)).toBe("testing TESTING testing AGAIN again AGAIN"); + expect(editor.lineTextForBufferRow(1)).toBe("testing TESTING testing AGAIN again AGAIN"); + + editor.undo(); + expect(editor.lineTextForBufferRow(0)).toBe("testing TESTING testing ANOTHER another "); + expect(editor.lineTextForBufferRow(1)).toBe("testing TESTING testing ANOTHER another "); + + editor.undo(); + expect(editor.lineTextForBufferRow(0)).toBe("placeholder PLACEHOLDER ANOTHER another "); + expect(editor.lineTextForBufferRow(1)).toBe("placeholder PLACEHOLDER ANOTHER another "); + + editor.redo(); + expect(editor.lineTextForBufferRow(0)).toBe("testing TESTING testing ANOTHER another "); + expect(editor.lineTextForBufferRow(1)).toBe("testing TESTING testing ANOTHER another "); + + editor.redo(); + expect(editor.lineTextForBufferRow(0)).toBe("testing TESTING testing AGAIN again AGAIN"); + return expect(editor.lineTextForBufferRow(1)).toBe("testing TESTING testing AGAIN again AGAIN"); + }); + + return describe("when there are many tabstops", () => it("moves the cursors between the tab stops for their corresponding snippet when tab and shift-tab are pressed", function() { + editor.addCursorAtBufferPosition([7, 5]); + editor.addCursorAtBufferPosition([12, 2]); + editor.insertText('t11'); + simulateTabKeyEvent(); + + const cursors = editor.getCursors(); + expect(cursors.length).toEqual(3); + + expect(cursors[0].getBufferPosition()).toEqual([0, 3]); + expect(cursors[1].getBufferPosition()).toEqual([7, 8]); + expect(cursors[2].getBufferPosition()).toEqual([12, 5]); + expect(cursors[0].selection.isEmpty()).toBe(true); + expect(cursors[1].selection.isEmpty()).toBe(true); + expect(cursors[2].selection.isEmpty()).toBe(true); + + simulateTabKeyEvent(); + expect(cursors[0].getBufferPosition()).toEqual([0, 7]); + expect(cursors[1].getBufferPosition()).toEqual([7, 12]); + expect(cursors[2].getBufferPosition()).toEqual([12, 9]); + expect(cursors[0].selection.isEmpty()).toBe(false); + expect(cursors[1].selection.isEmpty()).toBe(false); + expect(cursors[2].selection.isEmpty()).toBe(false); + expect(cursors[0].selection.getText()).toEqual('two'); + expect(cursors[1].selection.getText()).toEqual('two'); + expect(cursors[2].selection.getText()).toEqual('two'); + + simulateTabKeyEvent(); + expect(cursors[0].getBufferPosition()).toEqual([0, 13]); + expect(cursors[1].getBufferPosition()).toEqual([7, 18]); + expect(cursors[2].getBufferPosition()).toEqual([12, 15]); + expect(cursors[0].selection.isEmpty()).toBe(true); + expect(cursors[1].selection.isEmpty()).toBe(true); + expect(cursors[2].selection.isEmpty()).toBe(true); + + simulateTabKeyEvent(); + expect(cursors[0].getBufferPosition()).toEqual([0, 0]); + expect(cursors[1].getBufferPosition()).toEqual([7, 5]); + expect(cursors[2].getBufferPosition()).toEqual([12, 2]); + expect(cursors[0].selection.isEmpty()).toBe(true); + expect(cursors[1].selection.isEmpty()).toBe(true); + return expect(cursors[2].selection.isEmpty()).toBe(true); + })); + }); + + describe("when the cursors do not share common snippet prefixes", () => it("inserts tabs as normal", function() { + editor.insertText('t9'); + editor.setCursorBufferPosition([12, 2]); + editor.insertText(' t8'); + editor.addCursorAtBufferPosition([0, 2]); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("t9 var quicksort = function () {"); + return expect(editor.lineTextForBufferRow(12)).toBe("}; t8 "); + })); + + return describe("when a snippet is triggered within an existing snippet expansion", () => it("ignores the snippet expansion and goes to the next tab stop", function() { + editor.addCursorAtBufferPosition([7, 5]); + editor.addCursorAtBufferPosition([12, 2]); + editor.insertText('t11'); + simulateTabKeyEvent(); + simulateTabKeyEvent(); + + editor.insertText('t1'); + simulateTabKeyEvent(); + + const cursors = editor.getCursors(); + expect(cursors.length).toEqual(3); + + expect(cursors[0].getBufferPosition()).toEqual([0, 12]); + expect(cursors[1].getBufferPosition()).toEqual([7, 17]); + expect(cursors[2].getBufferPosition()).toEqual([12, 14]); + expect(cursors[0].selection.isEmpty()).toBe(true); + expect(cursors[1].selection.isEmpty()).toBe(true); + expect(cursors[2].selection.isEmpty()).toBe(true); + expect(editor.lineTextForBufferRow(0)).toBe("one t1 threevar quicksort = function () {"); + expect(editor.lineTextForBufferRow(7)).toBe(" }one t1 three"); + return expect(editor.lineTextForBufferRow(12)).toBe("};one t1 three"); + })); + }); + + return describe("when the editor is not a pane item (regression)", () => it("handles tab stops correctly", function() { + editor = new TextEditor(); + atom.grammars.assignLanguageMode(editor, 'source.js'); + editorElement = editor.getElement(); + + editor.insertText('t2'); + simulateTabKeyEvent(); + editor.insertText('ABC'); + expect(editor.getText()).toContain('go here first:(ABC)'); + + editor.undo(); + editor.undo(); + expect(editor.getText()).toBe('t2'); + simulateTabKeyEvent(); + editor.insertText('ABC'); + return expect(editor.getText()).toContain('go here first:(ABC)'); + })); + }); + + describe("when atom://.atom/snippets is opened", () => it("opens ~/.atom/snippets.cson", function() { + jasmine.unspy(Snippets, 'getUserSnippetsPath'); + atom.workspace.destroyActivePaneItem(); + const configDirPath = temp.mkdirSync('atom-config-dir-'); + spyOn(atom, 'getConfigDirPath').andReturn(configDirPath); + atom.workspace.open('atom://.atom/snippets'); + + waitsFor(() => atom.workspace.getActiveTextEditor() != null); + + return runs(() => expect(atom.workspace.getActiveTextEditor().getURI()).toBe(path.join(configDirPath, 'snippets.cson'))); + })); + + describe("snippet insertion API", () => it("will automatically parse snippet definition and replace selection", function() { + editor.setSelectedBufferRange([[0, 4], [0, 13]]); + Snippets.insert("hello ${1:world}", editor); + + expect(editor.lineTextForBufferRow(0)).toBe("var hello world = function () {"); + return expect(editor.getSelectedBufferRange()).toEqual([[0, 10], [0, 15]]); +})); + + return describe("when the 'snippets:available' command is triggered", function() { + let availableSnippetsView = null; + + beforeEach(function() { + Snippets.add(__filename, { + ".source.js": { + "test": { + prefix: "test", + body: "${1:Test pass you will}, young " + }, + + "challenge": { + prefix: "chal", + body: "$1: ${2:To pass this challenge}" + } + } + } + ); + + delete Snippets.availableSnippetsView; + + atom.commands.dispatch(editorElement, "snippets:available"); + + waitsFor(() => atom.workspace.getModalPanels().length === 1); + + return runs(() => availableSnippetsView = atom.workspace.getModalPanels()[0].getItem()); + }); + + it("renders a select list of all available snippets", function() { + expect(availableSnippetsView.selectListView.getSelectedItem().prefix).toBe('test'); + expect(availableSnippetsView.selectListView.getSelectedItem().name).toBe('test'); + expect(availableSnippetsView.selectListView.getSelectedItem().bodyText).toBe('${1:Test pass you will}, young '); + + availableSnippetsView.selectListView.selectNext(); + + expect(availableSnippetsView.selectListView.getSelectedItem().prefix).toBe('chal'); + expect(availableSnippetsView.selectListView.getSelectedItem().name).toBe('challenge'); + return expect(availableSnippetsView.selectListView.getSelectedItem().bodyText).toBe('$1: ${2:To pass this challenge}'); + }); + + it("writes the selected snippet to the editor as snippet", function() { + availableSnippetsView.selectListView.confirmSelection(); + + expect(editor.getCursorScreenPosition()).toEqual([0, 18]); + expect(editor.getSelectedText()).toBe('Test pass you will'); + return expect(editor.lineTextForBufferRow(0)).toBe('Test pass you will, young var quicksort = function () {'); + }); + + return it("closes the dialog when triggered again", function() { + atom.commands.dispatch(availableSnippetsView.selectListView.refs.queryEditor.element, 'snippets:available'); + return expect(atom.workspace.getModalPanels().length).toBe(0); + }); + }); +}); From 38fea44d3d9a28df1957bea455f4b13fe088b59f Mon Sep 17 00:00:00 2001 From: Benjamin Gray Date: Sun, 29 Dec 2019 23:18:25 +1000 Subject: [PATCH 2/6] decaf - manual cleanup --- spec/body-parser-spec.coffee | 486 +++---- spec/body-parser-spec.js | 132 +- spec/snippet-loading-spec.coffee | 536 ++++---- spec/snippet-loading-spec.js | 208 +-- spec/snippets-spec.coffee | 2144 +++++++++++++++--------------- spec/snippets-spec.js | 1244 ++++++++--------- 6 files changed, 2383 insertions(+), 2367 deletions(-) diff --git a/spec/body-parser-spec.coffee b/spec/body-parser-spec.coffee index 2f8a28e..4840f0a 100644 --- a/spec/body-parser-spec.coffee +++ b/spec/body-parser-spec.coffee @@ -1,243 +1,243 @@ -BodyParser = require '../lib/snippet-body-parser' - -describe "Snippet Body Parser", -> - it "breaks a snippet body into lines, with each line containing tab stops at the appropriate position", -> - bodyTree = BodyParser.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 = BodyParser.parse """ - module ${1:ActiveRecord::${TM_FILENAME/(?:\\A|_)([A-Za-z0-9]+)(?:\\.rb)?/(?2::\\u$1)/g}} - """ - - expect(bodyTree).toEqual [ - "module ", - { - "index": 1, - "content": ["ActiveRecord::", ""] - } - ] - - it "skips escaped tabstops", -> - bodyTree = BodyParser.parse """ - snippet $1 escaped \\$2 \\\\$3 - """ - - expect(bodyTree).toEqual [ - "snippet ", - { - index: 1, - content: [] - }, - " escaped $2 \\", - { - index: 3, - content: [] - } - ] - - it "includes escaped right-braces", -> - bodyTree = BodyParser.parse """ - snippet ${1:{\\}} - """ - - expect(bodyTree).toEqual [ - "snippet ", - { - index: 1, - content: ["{}"] - } - ] - - it "parses a snippet with transformations", -> - bodyTree = BodyParser.parse """ - <${1:p}>$0 - """ - expect(bodyTree).toEqual [ - '<', - {index: 1, content: ['p']}, - '>', - {index: 0, content: []}, - '' - ] - - it "parses a snippet with multiple tab stops with transformations", -> - bodyTree = BodyParser.parse """ - ${1:placeholder} ${1/(.)/\\u$1/} $1 ${2:ANOTHER} ${2/^(.*)$/\\L$1/} $2 - """ - - expect(bodyTree).toEqual [ - {index: 1, content: ['placeholder']}, - ' ', - { - index: 1, - content: [], - substitution: { - find: /(.)/g, - replace: [ - {escape: 'u'}, - {backreference: 1} - ] - } - }, - ' ', - {index: 1, content: []}, - ' ', - {index: 2, content: ['ANOTHER']}, - ' ', - { - index: 2, - content: [], - substitution: { - find: /^(.*)$/g, - replace: [ - {escape: 'L'}, - {backreference: 1} - ] - } - }, - ' ', - {index: 2, content: []}, - ] - - - it "parses a snippet with transformations and mirrors", -> - bodyTree = BodyParser.parse """ - ${1:placeholder}\n${1/(.)/\\u$1/}\n$1 - """ - - expect(bodyTree).toEqual [ - {index: 1, content: ['placeholder']}, - '\n', - { - index: 1, - content: [], - substitution: { - find: /(.)/g, - replace: [ - {escape: 'u'}, - {backreference: 1} - ] - } - }, - '\n', - {index: 1, content: []} - ] - - it "parses a snippet with a format string and case-control flags", -> - bodyTree = BodyParser.parse """ - <${1:p}>$0 - """ - - expect(bodyTree).toEqual [ - '<', - {index: 1, content: ['p']}, - '>', - {index: 0, content: []}, - '' - ] - - it "parses a snippet with an escaped forward slash in a transform", -> - # Annoyingly, a forward slash needs to be double-backslashed just like the - # other escapes. - bodyTree = BodyParser.parse """ - <${1:p}>$0 - """ - - expect(bodyTree).toEqual [ - '<', - {index: 1, content: ['p']}, - '>', - {index: 0, content: []}, - '' - ] - - it "parses a snippet with a placeholder that mirrors another tab stop's content", -> - bodyTree = BodyParser.parse """ - $4console.${3:log}('${2:$1}', $1);$0 - """ - - expect(bodyTree).toEqual [ - {index: 4, content: []}, - 'console.', - {index: 3, content: ['log']}, - '(\'', - { - index: 2, content: [ - {index: 1, content: []} - ] - }, - '\', ', - {index: 1, content: []}, - ');', - {index: 0, content: []} - ] - - it "parses a snippet with a placeholder that mixes text and tab stop references", -> - bodyTree = BodyParser.parse """ - $4console.${3:log}('${2:uh $1}', $1);$0 - """ - - expect(bodyTree).toEqual [ - {index: 4, content: []}, - 'console.', - {index: 3, content: ['log']}, - '(\'', - { - index: 2, content: [ - 'uh ', - {index: 1, content: []} - ] - }, - '\', ', - {index: 1, content: []}, - ');', - {index: 0, content: []} - ] +# BodyParser = require '../lib/snippet-body-parser' +# +# describe "Snippet Body Parser", -> +# it "breaks a snippet body into lines, with each line containing tab stops at the appropriate position", -> +# bodyTree = BodyParser.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 = BodyParser.parse """ +# module ${1:ActiveRecord::${TM_FILENAME/(?:\\A|_)([A-Za-z0-9]+)(?:\\.rb)?/(?2::\\u$1)/g}} +# """ +# +# expect(bodyTree).toEqual [ +# "module ", +# { +# "index": 1, +# "content": ["ActiveRecord::", ""] +# } +# ] +# +# it "skips escaped tabstops", -> +# bodyTree = BodyParser.parse """ +# snippet $1 escaped \\$2 \\\\$3 +# """ +# +# expect(bodyTree).toEqual [ +# "snippet ", +# { +# index: 1, +# content: [] +# }, +# " escaped $2 \\", +# { +# index: 3, +# content: [] +# } +# ] +# +# it "includes escaped right-braces", -> +# bodyTree = BodyParser.parse """ +# snippet ${1:{\\}} +# """ +# +# expect(bodyTree).toEqual [ +# "snippet ", +# { +# index: 1, +# content: ["{}"] +# } +# ] +# +# it "parses a snippet with transformations", -> +# bodyTree = BodyParser.parse """ +# <${1:p}>$0 +# """ +# expect(bodyTree).toEqual [ +# '<', +# {index: 1, content: ['p']}, +# '>', +# {index: 0, content: []}, +# '' +# ] +# +# it "parses a snippet with multiple tab stops with transformations", -> +# bodyTree = BodyParser.parse """ +# ${1:placeholder} ${1/(.)/\\u$1/} $1 ${2:ANOTHER} ${2/^(.*)$/\\L$1/} $2 +# """ +# +# expect(bodyTree).toEqual [ +# {index: 1, content: ['placeholder']}, +# ' ', +# { +# index: 1, +# content: [], +# substitution: { +# find: /(.)/g, +# replace: [ +# {escape: 'u'}, +# {backreference: 1} +# ] +# } +# }, +# ' ', +# {index: 1, content: []}, +# ' ', +# {index: 2, content: ['ANOTHER']}, +# ' ', +# { +# index: 2, +# content: [], +# substitution: { +# find: /^(.*)$/g, +# replace: [ +# {escape: 'L'}, +# {backreference: 1} +# ] +# } +# }, +# ' ', +# {index: 2, content: []}, +# ] +# +# +# it "parses a snippet with transformations and mirrors", -> +# bodyTree = BodyParser.parse """ +# ${1:placeholder}\n${1/(.)/\\u$1/}\n$1 +# """ +# +# expect(bodyTree).toEqual [ +# {index: 1, content: ['placeholder']}, +# '\n', +# { +# index: 1, +# content: [], +# substitution: { +# find: /(.)/g, +# replace: [ +# {escape: 'u'}, +# {backreference: 1} +# ] +# } +# }, +# '\n', +# {index: 1, content: []} +# ] +# +# it "parses a snippet with a format string and case-control flags", -> +# bodyTree = BodyParser.parse """ +# <${1:p}>$0 +# """ +# +# expect(bodyTree).toEqual [ +# '<', +# {index: 1, content: ['p']}, +# '>', +# {index: 0, content: []}, +# '' +# ] +# +# it "parses a snippet with an escaped forward slash in a transform", -> +# # Annoyingly, a forward slash needs to be double-backslashed just like the +# # other escapes. +# bodyTree = BodyParser.parse """ +# <${1:p}>$0 +# """ +# +# expect(bodyTree).toEqual [ +# '<', +# {index: 1, content: ['p']}, +# '>', +# {index: 0, content: []}, +# '' +# ] +# +# it "parses a snippet with a placeholder that mirrors another tab stop's content", -> +# bodyTree = BodyParser.parse """ +# $4console.${3:log}('${2:$1}', $1);$0 +# """ +# +# expect(bodyTree).toEqual [ +# {index: 4, content: []}, +# 'console.', +# {index: 3, content: ['log']}, +# '(\'', +# { +# index: 2, content: [ +# {index: 1, content: []} +# ] +# }, +# '\', ', +# {index: 1, content: []}, +# ');', +# {index: 0, content: []} +# ] +# +# it "parses a snippet with a placeholder that mixes text and tab stop references", -> +# bodyTree = BodyParser.parse """ +# $4console.${3:log}('${2:uh $1}', $1);$0 +# """ +# +# expect(bodyTree).toEqual [ +# {index: 4, content: []}, +# 'console.', +# {index: 3, content: ['log']}, +# '(\'', +# { +# index: 2, content: [ +# 'uh ', +# {index: 1, content: []} +# ] +# }, +# '\', ', +# {index: 1, content: []}, +# ');', +# {index: 0, content: []} +# ] diff --git a/spec/body-parser-spec.js b/spec/body-parser-spec.js index 475dadd..a5c8a54 100644 --- a/spec/body-parser-spec.js +++ b/spec/body-parser-spec.js @@ -1,19 +1,14 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ const BodyParser = require('../lib/snippet-body-parser'); -describe("Snippet Body Parser", function() { - it("breaks a snippet body into lines, with each line containing tab stops at the appropriate position", function() { +describe("Snippet Body Parser", () => { + it("breaks a snippet body into lines, with each line containing tab stops at the appropriate position", () => { const bodyTree = BodyParser.parse(`\ the quick brown $1fox \${2:jumped \${3:over} }the \${4:lazy} dog\ ` ); - return expect(bodyTree).toEqual([ + expect(bodyTree).toEqual([ "the quick brown ", {index: 1, content: []}, "fox ", @@ -29,30 +24,22 @@ the quick brown $1fox \${2:jumped \${3:over} {index: 4, content: ["lazy"]}, " dog" ]); -}); + }); - it("removes interpolated variables in placeholder text (we don't currently support it)", function() { - const bodyTree = BodyParser.parse(`\ -module \${1:ActiveRecord::\${TM_FILENAME/(?:\\A|_)([A-Za-z0-9]+)(?:\\.rb)?/(?2::\\u$1)/g}}\ -` - ); - - return expect(bodyTree).toEqual([ + it("removes interpolated variables in placeholder text (we don't currently support it)", () => { + const bodyTree = BodyParser.parse("module ${1:ActiveRecord::${TM_FILENAME/(?:\\A|_)([A-Za-z0-9]+)(?:\\.rb)?/(?2::\\u$1)/g}}"); + expect(bodyTree).toEqual([ "module ", { "index": 1, "content": ["ActiveRecord::", ""] } ]); -}); + }); - it("skips escaped tabstops", function() { - const bodyTree = BodyParser.parse(`\ -snippet $1 escaped \\$2 \\\\$3\ -` - ); - - return expect(bodyTree).toEqual([ + it("skips escaped tabstops", () => { + const bodyTree = BodyParser.parse("snippet $1 escaped \\$2 \\\\$3"); + expect(bodyTree).toEqual([ "snippet ", { index: 1, @@ -64,29 +51,22 @@ snippet $1 escaped \\$2 \\\\$3\ content: [] } ]); -}); + }); - it("includes escaped right-braces", function() { - const bodyTree = BodyParser.parse(`\ -snippet \${1:{\\}}\ -` - ); - - return expect(bodyTree).toEqual([ + it("includes escaped right-braces", () => { + const bodyTree = BodyParser.parse("snippet ${1:{\\}}"); + expect(bodyTree).toEqual([ "snippet ", { index: 1, content: ["{}"] } ]); -}); + }); - it("parses a snippet with transformations", function() { - const bodyTree = BodyParser.parse(`\ -<\${1:p}>$0\ -` - ); - return expect(bodyTree).toEqual([ + it("parses a snippet with transformations", () => { + const bodyTree = BodyParser.parse("<${1:p}>$0"); + expect(bodyTree).toEqual([ '<', {index: 1, content: ['p']}, '>', @@ -95,15 +75,11 @@ snippet \${1:{\\}}\ {index: 1, content: [], substitution: {find: /f/g, replace: ['F']}}, '>' ]); -}); - - it("parses a snippet with multiple tab stops with transformations", function() { - const bodyTree = BodyParser.parse(`\ -\${1:placeholder} \${1/(.)/\\u$1/} $1 \${2:ANOTHER} \${2/^(.*)$/\\L$1/} $2\ -` - ); + }); - return expect(bodyTree).toEqual([ + it("parses a snippet with multiple tab stops with transformations", () => { + const bodyTree = BodyParser.parse("${1:placeholder} ${1/(.)/\\u$1/} $1 ${2:ANOTHER} ${2/^(.*)$/\\L$1/} $2"); + expect(bodyTree).toEqual([ {index: 1, content: ['placeholder']}, ' ', { @@ -136,16 +112,12 @@ snippet \${1:{\\}}\ ' ', {index: 2, content: []}, ]); -}); + }); - it("parses a snippet with transformations and mirrors", function() { - const bodyTree = BodyParser.parse(`\ -\${1:placeholder}\n\${1/(.)/\\u$1/}\n$1\ -` - ); - - return expect(bodyTree).toEqual([ + it("parses a snippet with transformations and mirrors", () => { + const bodyTree = BodyParser.parse("${1:placeholder}\n\${1/(.)/\\u$1/}\n$1"); + expect(bodyTree).toEqual([ {index: 1, content: ['placeholder']}, '\n', { @@ -162,15 +134,11 @@ snippet \${1:{\\}}\ '\n', {index: 1, content: []} ]); -}); + }); - it("parses a snippet with a format string and case-control flags", function() { - const bodyTree = BodyParser.parse(`\ -<\${1:p}>$0\ -` - ); - - return expect(bodyTree).toEqual([ + it("parses a snippet with a format string and case-control flags", () => { + const bodyTree = BodyParser.parse("<\${1:p}>$0"); + expect(bodyTree).toEqual([ '<', {index: 1, content: ['p']}, '>', @@ -190,17 +158,13 @@ snippet \${1:{\\}}\ }, '>' ]); -}); + }); - it("parses a snippet with an escaped forward slash in a transform", function() { + it("parses a snippet with an escaped forward slash in a transform", () => { // Annoyingly, a forward slash needs to be double-backslashed just like the // other escapes. - const bodyTree = BodyParser.parse(`\ -<\${1:p}>$0\ -` - ); - - return expect(bodyTree).toEqual([ + const bodyTree = BodyParser.parse("<${1:p}>$0"); + expect(bodyTree).toEqual([ '<', {index: 1, content: ['p']}, '>', @@ -220,15 +184,11 @@ snippet \${1:{\\}}\ }, '>' ]); -}); - - it("parses a snippet with a placeholder that mirrors another tab stop's content", function() { - const bodyTree = BodyParser.parse(`\ -$4console.\${3:log}('\${2:$1}', $1);$0\ -` - ); + }); - return expect(bodyTree).toEqual([ + it("parses a snippet with a placeholder that mirrors another tab stop's content", () => { + const bodyTree = BodyParser.parse("$4console.\${3:log}('\${2:$1}', $1);$0"); + expect(bodyTree).toEqual([ {index: 4, content: []}, 'console.', {index: 3, content: ['log']}, @@ -243,15 +203,11 @@ $4console.\${3:log}('\${2:$1}', $1);$0\ ');', {index: 0, content: []} ]); -}); - - return it("parses a snippet with a placeholder that mixes text and tab stop references", function() { - const bodyTree = BodyParser.parse(`\ -$4console.\${3:log}('\${2:uh $1}', $1);$0\ -` - ); + }); - return expect(bodyTree).toEqual([ + it("parses a snippet with a placeholder that mixes text and tab stop references", () => { + const bodyTree = BodyParser.parse("$4console.${3:log}('${2:uh $1}', $1);$0"); + expect(bodyTree).toEqual([ {index: 4, content: []}, 'console.', {index: 3, content: ['log']}, @@ -267,5 +223,5 @@ $4console.\${3:log}('\${2:uh $1}', $1);$0\ ');', {index: 0, content: []} ]); -}); + }); }); diff --git a/spec/snippet-loading-spec.coffee b/spec/snippet-loading-spec.coffee index 12a6c2a..273186f 100644 --- a/spec/snippet-loading-spec.coffee +++ b/spec/snippet-loading-spec.coffee @@ -1,268 +1,268 @@ -path = require 'path' -fs = require 'fs-plus' -temp = require('temp').track() - -describe "Snippet Loading", -> - [configDirPath, snippetsService] = [] - - beforeEach -> - configDirPath = temp.mkdirSync('atom-config-dir-') - spyOn(atom, 'getConfigDirPath').andReturn configDirPath - - spyOn(console, 'warn') - spyOn(atom.notifications, 'addError') if atom.notifications? - - spyOn(atom.packages, 'getLoadedPackages').andReturn [ - atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-snippets')) - atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-broken-snippets')), - ] - - afterEach -> - waitsForPromise -> - Promise.resolve(atom.packages.deactivatePackages('snippets')) - runs -> - jasmine.unspy(atom.packages, 'getLoadedPackages') - - activateSnippetsPackage = -> - waitsForPromise -> - atom.packages.activatePackage("snippets").then ({mainModule}) -> - snippetsService = mainModule.provideSnippets() - mainModule.loaded = false - - waitsFor "all snippets to load", 3000, -> - snippetsService.bundledSnippetsLoaded() - - it "loads the bundled snippet template snippets", -> - activateSnippetsPackage() - - runs -> - jsonSnippet = snippetsService.snippetsForScopes(['.source.json'])['snip'] - expect(jsonSnippet.name).toBe 'Atom Snippet' - expect(jsonSnippet.prefix).toBe 'snip' - expect(jsonSnippet.body).toContain '"prefix":' - expect(jsonSnippet.body).toContain '"body":' - expect(jsonSnippet.tabStopList.length).toBeGreaterThan(0) - - csonSnippet = snippetsService.snippetsForScopes(['.source.coffee'])['snip'] - expect(csonSnippet.name).toBe 'Atom Snippet' - expect(csonSnippet.prefix).toBe 'snip' - expect(csonSnippet.body).toContain "'prefix':" - expect(csonSnippet.body).toContain "'body':" - expect(csonSnippet.tabStopList.length).toBeGreaterThan(0) - - it "loads non-hidden snippet files from atom packages with snippets directories", -> - activateSnippetsPackage() - - runs -> - snippet = snippetsService.snippetsForScopes(['.test'])['test'] - expect(snippet.prefix).toBe 'test' - expect(snippet.body).toBe 'testing 123' - - snippet = snippetsService.snippetsForScopes(['.test'])['testd'] - expect(snippet.prefix).toBe 'testd' - expect(snippet.body).toBe 'testing 456' - expect(snippet.description).toBe 'a description' - expect(snippet.descriptionMoreURL).toBe 'http://google.com' - - snippet = snippetsService.snippetsForScopes(['.test'])['testlabelleft'] - expect(snippet.prefix).toBe 'testlabelleft' - expect(snippet.body).toBe 'testing 456' - expect(snippet.leftLabel).toBe 'a label' - - snippet = snippetsService.snippetsForScopes(['.test'])['testhtmllabels'] - expect(snippet.prefix).toBe 'testhtmllabels' - expect(snippet.body).toBe 'testing 456' - expect(snippet.leftLabelHTML).toBe 'Label' - expect(snippet.rightLabelHTML).toBe 'Label' - - it "logs a warning if package snippets files cannot be parsed", -> - activateSnippetsPackage() - - runs -> - # Warn about invalid-file, but don't even try to parse a hidden file - expect(console.warn.calls.length).toBeGreaterThan 0 - expect(console.warn.mostRecentCall.args[0]).toMatch(/Error reading.*package-with-broken-snippets/) - - describe "::loadPackageSnippets(callback)", -> - beforeEach -> - # simulate a list of packages where the javascript core package is returned at the end - atom.packages.getLoadedPackages.andReturn [ - atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-snippets')) - atom.packages.loadPackage('language-javascript') - ] - - it "allows other packages to override core packages' snippets", -> - waitsForPromise -> - atom.packages.activatePackage("language-javascript") - - activateSnippetsPackage() - - runs -> - snippet = snippetsService.snippetsForScopes(['.source.js'])['log'] - expect(snippet.body).toBe "from-a-community-package" - - describe "::onDidLoadSnippets(callback)", -> - it "invokes listeners when all snippets are loaded", -> - loadedCallback = null - - waitsFor "package to activate", (done) -> - atom.packages.activatePackage("snippets").then ({mainModule}) -> - mainModule.onDidLoadSnippets(loadedCallback = jasmine.createSpy('onDidLoadSnippets callback')) - done() - - waitsFor "onDidLoad callback to be called", -> loadedCallback.callCount > 0 - - describe "when ~/.atom/snippets.json exists", -> - beforeEach -> - fs.writeFileSync path.join(configDirPath, 'snippets.json'), """ - { - ".foo": { - "foo snippet": { - "prefix": "foo", - "body": "bar1" - } - } - } - """ - activateSnippetsPackage() - - it "loads the snippets from that file", -> - snippet = null - - waitsFor -> - snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] - - runs -> - expect(snippet.name).toBe 'foo snippet' - expect(snippet.prefix).toBe "foo" - expect(snippet.body).toBe "bar1" - - describe "when that file changes", -> - it "reloads the snippets", -> - fs.writeFileSync path.join(configDirPath, 'snippets.json'), """ - { - ".foo": { - "foo snippet": { - "prefix": "foo", - "body": "bar2" - } - } - } - """ - - waitsFor "snippets to be changed", -> - snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] - snippet?.body is 'bar2' - - runs -> - fs.writeFileSync path.join(configDirPath, 'snippets.json'), "" - - waitsFor "snippets to be removed", -> - not snippetsService.snippetsForScopes(['.foo'])['foo'] - - describe "when ~/.atom/snippets.cson exists", -> - beforeEach -> - fs.writeFileSync path.join(configDirPath, 'snippets.cson'), """ - ".foo": - "foo snippet": - "prefix": "foo" - "body": "bar1" - """ - activateSnippetsPackage() - - it "loads the snippets from that file", -> - snippet = null - - waitsFor -> - snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] - - runs -> - expect(snippet.name).toBe 'foo snippet' - expect(snippet.prefix).toBe "foo" - expect(snippet.body).toBe "bar1" - - describe "when that file changes", -> - it "reloads the snippets", -> - fs.writeFileSync path.join(configDirPath, 'snippets.cson'), """ - ".foo": - "foo snippet": - "prefix": "foo" - "body": "bar2" - """ - - waitsFor "snippets to be changed", -> - snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] - snippet?.body is 'bar2' - - runs -> - fs.writeFileSync path.join(configDirPath, 'snippets.cson'), "" - - waitsFor "snippets to be removed", -> - snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] - not snippet? - - it "notifies the user when the user snippets file cannot be loaded", -> - fs.writeFileSync path.join(configDirPath, 'snippets.cson'), """ - ".junk"::: - """ - - activateSnippetsPackage() - - runs -> - expect(console.warn).toHaveBeenCalled() - expect(atom.notifications.addError).toHaveBeenCalled() if atom.notifications? - - describe "packages-with-snippets-disabled feature", -> - it "disables no snippets if the config option is empty", -> - originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') - atom.config.set('core.packagesWithSnippetsDisabled', []) - - activateSnippetsPackage() - runs -> - snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) - expect(Object.keys(snippets).length).toBe 1 - atom.config.set('core.packagesWithSnippetsDisabled', originalConfig) - - it "still includes a disabled package's snippets in the list of unparsed snippets", -> - originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') - atom.config.set('core.packagesWithSnippetsDisabled', []) - - activateSnippetsPackage() - runs -> - atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets']) - allSnippets = snippetsService.getUnparsedSnippets() - scopedSnippet = allSnippets.find (s) -> - s.selectorString is '.package-with-snippets-unique-scope' - expect(scopedSnippet).not.toBe undefined - originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') - - it "never loads a package's snippets when that package is disabled in config", -> - originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') - atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets']) - - activateSnippetsPackage() - runs -> - snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) - expect(Object.keys(snippets).length).toBe 0 - atom.config.set('core.packagesWithSnippetsDisabled', originalConfig) - - it "unloads and/or reloads snippets from a package if the config option is changed after activation", -> - originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') - atom.config.set('core.packagesWithSnippetsDisabled', []) - - activateSnippetsPackage() - runs -> - snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) - expect(Object.keys(snippets).length).toBe 1 - - # Disable it. - atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets']) - snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) - expect(Object.keys(snippets).length).toBe 0 - - # Re-enable it. - atom.config.set('core.packagesWithSnippetsDisabled', []) - snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) - expect(Object.keys(snippets).length).toBe 1 - - atom.config.set('core.packagesWithSnippetsDisabled', originalConfig) +# path = require 'path' +# fs = require 'fs-plus' +# temp = require('temp').track() +# +# describe "Snippet Loading", -> +# [configDirPath, snippetsService] = [] +# +# beforeEach -> +# configDirPath = temp.mkdirSync('atom-config-dir-') +# spyOn(atom, 'getConfigDirPath').andReturn configDirPath +# +# spyOn(console, 'warn') +# spyOn(atom.notifications, 'addError') if atom.notifications? +# +# spyOn(atom.packages, 'getLoadedPackages').andReturn [ +# atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-snippets')) +# atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-broken-snippets')), +# ] +# +# afterEach -> +# waitsForPromise -> +# Promise.resolve(atom.packages.deactivatePackages('snippets')) +# runs -> +# jasmine.unspy(atom.packages, 'getLoadedPackages') +# +# activateSnippetsPackage = -> +# waitsForPromise -> +# atom.packages.activatePackage("snippets").then ({mainModule}) -> +# snippetsService = mainModule.provideSnippets() +# mainModule.loaded = false +# +# waitsFor "all snippets to load", 3000, -> +# snippetsService.bundledSnippetsLoaded() +# +# it "loads the bundled snippet template snippets", -> +# activateSnippetsPackage() +# +# runs -> +# jsonSnippet = snippetsService.snippetsForScopes(['.source.json'])['snip'] +# expect(jsonSnippet.name).toBe 'Atom Snippet' +# expect(jsonSnippet.prefix).toBe 'snip' +# expect(jsonSnippet.body).toContain '"prefix":' +# expect(jsonSnippet.body).toContain '"body":' +# expect(jsonSnippet.tabStopList.length).toBeGreaterThan(0) +# +# csonSnippet = snippetsService.snippetsForScopes(['.source.coffee'])['snip'] +# expect(csonSnippet.name).toBe 'Atom Snippet' +# expect(csonSnippet.prefix).toBe 'snip' +# expect(csonSnippet.body).toContain "'prefix':" +# expect(csonSnippet.body).toContain "'body':" +# expect(csonSnippet.tabStopList.length).toBeGreaterThan(0) +# +# it "loads non-hidden snippet files from atom packages with snippets directories", -> +# activateSnippetsPackage() +# +# runs -> +# snippet = snippetsService.snippetsForScopes(['.test'])['test'] +# expect(snippet.prefix).toBe 'test' +# expect(snippet.body).toBe 'testing 123' +# +# snippet = snippetsService.snippetsForScopes(['.test'])['testd'] +# expect(snippet.prefix).toBe 'testd' +# expect(snippet.body).toBe 'testing 456' +# expect(snippet.description).toBe 'a description' +# expect(snippet.descriptionMoreURL).toBe 'http://google.com' +# +# snippet = snippetsService.snippetsForScopes(['.test'])['testlabelleft'] +# expect(snippet.prefix).toBe 'testlabelleft' +# expect(snippet.body).toBe 'testing 456' +# expect(snippet.leftLabel).toBe 'a label' +# +# snippet = snippetsService.snippetsForScopes(['.test'])['testhtmllabels'] +# expect(snippet.prefix).toBe 'testhtmllabels' +# expect(snippet.body).toBe 'testing 456' +# expect(snippet.leftLabelHTML).toBe 'Label' +# expect(snippet.rightLabelHTML).toBe 'Label' +# +# it "logs a warning if package snippets files cannot be parsed", -> +# activateSnippetsPackage() +# +# runs -> +# # Warn about invalid-file, but don't even try to parse a hidden file +# expect(console.warn.calls.length).toBeGreaterThan 0 +# expect(console.warn.mostRecentCall.args[0]).toMatch(/Error reading.*package-with-broken-snippets/) +# +# describe "::loadPackageSnippets(callback)", -> +# beforeEach -> +# # simulate a list of packages where the javascript core package is returned at the end +# atom.packages.getLoadedPackages.andReturn [ +# atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-snippets')) +# atom.packages.loadPackage('language-javascript') +# ] +# +# it "allows other packages to override core packages' snippets", -> +# waitsForPromise -> +# atom.packages.activatePackage("language-javascript") +# +# activateSnippetsPackage() +# +# runs -> +# snippet = snippetsService.snippetsForScopes(['.source.js'])['log'] +# expect(snippet.body).toBe "from-a-community-package" +# +# describe "::onDidLoadSnippets(callback)", -> +# it "invokes listeners when all snippets are loaded", -> +# loadedCallback = null +# +# waitsFor "package to activate", (done) -> +# atom.packages.activatePackage("snippets").then ({mainModule}) -> +# mainModule.onDidLoadSnippets(loadedCallback = jasmine.createSpy('onDidLoadSnippets callback')) +# done() +# +# waitsFor "onDidLoad callback to be called", -> loadedCallback.callCount > 0 +# +# describe "when ~/.atom/snippets.json exists", -> +# beforeEach -> +# fs.writeFileSync path.join(configDirPath, 'snippets.json'), """ +# { +# ".foo": { +# "foo snippet": { +# "prefix": "foo", +# "body": "bar1" +# } +# } +# } +# """ +# activateSnippetsPackage() +# +# it "loads the snippets from that file", -> +# snippet = null +# +# waitsFor -> +# snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] +# +# runs -> +# expect(snippet.name).toBe 'foo snippet' +# expect(snippet.prefix).toBe "foo" +# expect(snippet.body).toBe "bar1" +# +# describe "when that file changes", -> +# it "reloads the snippets", -> +# fs.writeFileSync path.join(configDirPath, 'snippets.json'), """ +# { +# ".foo": { +# "foo snippet": { +# "prefix": "foo", +# "body": "bar2" +# } +# } +# } +# """ +# +# waitsFor "snippets to be changed", -> +# snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] +# snippet?.body is 'bar2' +# +# runs -> +# fs.writeFileSync path.join(configDirPath, 'snippets.json'), "" +# +# waitsFor "snippets to be removed", -> +# not snippetsService.snippetsForScopes(['.foo'])['foo'] +# +# describe "when ~/.atom/snippets.cson exists", -> +# beforeEach -> +# fs.writeFileSync path.join(configDirPath, 'snippets.cson'), """ +# ".foo": +# "foo snippet": +# "prefix": "foo" +# "body": "bar1" +# """ +# activateSnippetsPackage() +# +# it "loads the snippets from that file", -> +# snippet = null +# +# waitsFor -> +# snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] +# +# runs -> +# expect(snippet.name).toBe 'foo snippet' +# expect(snippet.prefix).toBe "foo" +# expect(snippet.body).toBe "bar1" +# +# describe "when that file changes", -> +# it "reloads the snippets", -> +# fs.writeFileSync path.join(configDirPath, 'snippets.cson'), """ +# ".foo": +# "foo snippet": +# "prefix": "foo" +# "body": "bar2" +# """ +# +# waitsFor "snippets to be changed", -> +# snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] +# snippet?.body is 'bar2' +# +# runs -> +# fs.writeFileSync path.join(configDirPath, 'snippets.cson'), "" +# +# waitsFor "snippets to be removed", -> +# snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] +# not snippet? +# +# it "notifies the user when the user snippets file cannot be loaded", -> +# fs.writeFileSync path.join(configDirPath, 'snippets.cson'), """ +# ".junk"::: +# """ +# +# activateSnippetsPackage() +# +# runs -> +# expect(console.warn).toHaveBeenCalled() +# expect(atom.notifications.addError).toHaveBeenCalled() if atom.notifications? +# +# describe "packages-with-snippets-disabled feature", -> +# it "disables no snippets if the config option is empty", -> +# originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') +# atom.config.set('core.packagesWithSnippetsDisabled', []) +# +# activateSnippetsPackage() +# runs -> +# snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) +# expect(Object.keys(snippets).length).toBe 1 +# atom.config.set('core.packagesWithSnippetsDisabled', originalConfig) +# +# it "still includes a disabled package's snippets in the list of unparsed snippets", -> +# originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') +# atom.config.set('core.packagesWithSnippetsDisabled', []) +# +# activateSnippetsPackage() +# runs -> +# atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets']) +# allSnippets = snippetsService.getUnparsedSnippets() +# scopedSnippet = allSnippets.find (s) -> +# s.selectorString is '.package-with-snippets-unique-scope' +# expect(scopedSnippet).not.toBe undefined +# originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') +# +# it "never loads a package's snippets when that package is disabled in config", -> +# originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') +# atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets']) +# +# activateSnippetsPackage() +# runs -> +# snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) +# expect(Object.keys(snippets).length).toBe 0 +# atom.config.set('core.packagesWithSnippetsDisabled', originalConfig) +# +# it "unloads and/or reloads snippets from a package if the config option is changed after activation", -> +# originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') +# atom.config.set('core.packagesWithSnippetsDisabled', []) +# +# activateSnippetsPackage() +# runs -> +# snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) +# expect(Object.keys(snippets).length).toBe 1 +# +# # Disable it. +# atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets']) +# snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) +# expect(Object.keys(snippets).length).toBe 0 +# +# # Re-enable it. +# atom.config.set('core.packagesWithSnippetsDisabled', []) +# snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) +# expect(Object.keys(snippets).length).toBe 1 +# +# atom.config.set('core.packagesWithSnippetsDisabled', originalConfig) diff --git a/spec/snippet-loading-spec.js b/spec/snippet-loading-spec.js index 35ea4b4..0772ce1 100644 --- a/spec/snippet-loading-spec.js +++ b/spec/snippet-loading-spec.js @@ -1,47 +1,41 @@ -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ const path = require('path'); const fs = require('fs-plus'); const temp = require('temp').track(); -describe("Snippet Loading", function() { - let [configDirPath, snippetsService] = Array.from([]); +describe("Snippet Loading", () => { + let configDirPath, snippetsService; - beforeEach(function() { + beforeEach(() => { configDirPath = temp.mkdirSync('atom-config-dir-'); spyOn(atom, 'getConfigDirPath').andReturn(configDirPath); spyOn(console, 'warn'); if (atom.notifications != null) { spyOn(atom.notifications, 'addError'); } - return spyOn(atom.packages, 'getLoadedPackages').andReturn([ + spyOn(atom.packages, 'getLoadedPackages').andReturn([ atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-snippets')), atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-broken-snippets')), - ]);}); + ]); + }); - afterEach(function() { + afterEach(() => { waitsForPromise(() => Promise.resolve(atom.packages.deactivatePackages('snippets'))); - return runs(() => jasmine.unspy(atom.packages, 'getLoadedPackages')); + runs(() => jasmine.unspy(atom.packages, 'getLoadedPackages')); }); - const activateSnippetsPackage = function() { - waitsForPromise(() => atom.packages.activatePackage("snippets").then(function({mainModule}) { + const activateSnippetsPackage = () => { + waitsForPromise(() => atom.packages.activatePackage("snippets").then(({mainModule}) => { snippetsService = mainModule.provideSnippets(); - return mainModule.loaded = false; + mainModule.loaded = false; })); - return waitsFor("all snippets to load", 3000, () => snippetsService.bundledSnippetsLoaded()); + waitsFor("all snippets to load", 3000, () => snippetsService.bundledSnippetsLoaded()); }; - it("loads the bundled snippet template snippets", function() { + it("loads the bundled snippet template snippets", () => { activateSnippetsPackage(); - return runs(function() { + runs(() => { const jsonSnippet = snippetsService.snippetsForScopes(['.source.json'])['snip']; expect(jsonSnippet.name).toBe('Atom Snippet'); expect(jsonSnippet.prefix).toBe('snip'); @@ -54,14 +48,14 @@ describe("Snippet Loading", function() { expect(csonSnippet.prefix).toBe('snip'); expect(csonSnippet.body).toContain("'prefix':"); expect(csonSnippet.body).toContain("'body':"); - return expect(csonSnippet.tabStopList.length).toBeGreaterThan(0); + expect(csonSnippet.tabStopList.length).toBeGreaterThan(0); }); }); - it("loads non-hidden snippet files from atom packages with snippets directories", function() { + it("loads non-hidden snippet files from atom packages with snippets directories", () => { activateSnippetsPackage(); - return runs(function() { + runs(() => { let snippet = snippetsService.snippetsForScopes(['.test'])['test']; expect(snippet.prefix).toBe('test'); expect(snippet.body).toBe('testing 123'); @@ -81,52 +75,55 @@ describe("Snippet Loading", function() { expect(snippet.prefix).toBe('testhtmllabels'); expect(snippet.body).toBe('testing 456'); expect(snippet.leftLabelHTML).toBe('Label'); - return expect(snippet.rightLabelHTML).toBe('Label'); + expect(snippet.rightLabelHTML).toBe('Label'); }); }); - it("logs a warning if package snippets files cannot be parsed", function() { + it("logs a warning if package snippets files cannot be parsed", () => { activateSnippetsPackage(); - return runs(function() { + runs(() => { // Warn about invalid-file, but don't even try to parse a hidden file expect(console.warn.calls.length).toBeGreaterThan(0); - return expect(console.warn.mostRecentCall.args[0]).toMatch(/Error reading.*package-with-broken-snippets/); + expect(console.warn.mostRecentCall.args[0]).toMatch(/Error reading.*package-with-broken-snippets/); }); }); - describe("::loadPackageSnippets(callback)", function() { - beforeEach(() => // simulate a list of packages where the javascript core package is returned at the end - atom.packages.getLoadedPackages.andReturn([ - atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-snippets')), - atom.packages.loadPackage('language-javascript') - ])); + describe("::loadPackageSnippets(callback)", () => { + beforeEach(() => { // simulate a list of packages where the javascript core package is returned at the end + atom.packages.getLoadedPackages.andReturn([ + atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-snippets')), + atom.packages.loadPackage('language-javascript') + ]); + }); - return it("allows other packages to override core packages' snippets", function() { + it("allows other packages to override core packages' snippets", () => { waitsForPromise(() => atom.packages.activatePackage("language-javascript")); activateSnippetsPackage(); - return runs(function() { + runs(() => { const snippet = snippetsService.snippetsForScopes(['.source.js'])['log']; - return expect(snippet.body).toBe("from-a-community-package"); + expect(snippet.body).toBe("from-a-community-package"); }); }); }); - describe("::onDidLoadSnippets(callback)", () => it("invokes listeners when all snippets are loaded", function() { - let loadedCallback = null; + describe("::onDidLoadSnippets(callback)", () => { + it("invokes listeners when all snippets are loaded", () => { + let loadedCallback = null; - waitsFor("package to activate", done => atom.packages.activatePackage("snippets").then(function({mainModule}) { - mainModule.onDidLoadSnippets(loadedCallback = jasmine.createSpy('onDidLoadSnippets callback')); - return done(); - })); + waitsFor("package to activate", done => atom.packages.activatePackage("snippets").then(({mainModule}) => { + mainModule.onDidLoadSnippets(loadedCallback = jasmine.createSpy('onDidLoadSnippets callback')); + done(); + })); - return waitsFor("onDidLoad callback to be called", () => loadedCallback.callCount > 0); - })); + waitsFor("onDidLoad callback to be called", () => loadedCallback.callCount > 0); + }); + }); - describe("when ~/.atom/snippets.json exists", function() { - beforeEach(function() { + describe("when ~/.atom/snippets.json exists", () => { + beforeEach(() => { fs.writeFileSync(path.join(configDirPath, 'snippets.json'), `\ { ".foo": { @@ -138,23 +135,24 @@ describe("Snippet Loading", function() { }\ ` ); - return activateSnippetsPackage(); + activateSnippetsPackage(); }); - it("loads the snippets from that file", function() { + it("loads the snippets from that file", () => { let snippet = null; waitsFor(() => snippet = snippetsService.snippetsForScopes(['.foo'])['foo']); - return runs(function() { + runs(() => { expect(snippet.name).toBe('foo snippet'); expect(snippet.prefix).toBe("foo"); - return expect(snippet.body).toBe("bar1"); + expect(snippet.body).toBe("bar1"); }); }); - return describe("when that file changes", () => it("reloads the snippets", function() { - fs.writeFileSync(path.join(configDirPath, 'snippets.json'), `\ + describe("when that file changes", () => { + it("reloads the snippets", () => { + fs.writeFileSync(path.join(configDirPath, 'snippets.json'), `\ { ".foo": { "foo snippet": { @@ -164,21 +162,22 @@ describe("Snippet Loading", function() { } }\ ` - ); + ); - waitsFor("snippets to be changed", function() { - const snippet = snippetsService.snippetsForScopes(['.foo'])['foo']; - return (snippet != null ? snippet.body : undefined) === 'bar2'; - }); + waitsFor("snippets to be changed", () => { + const snippet = snippetsService.snippetsForScopes(['.foo'])['foo']; + return snippet && snippet.body === 'bar2'; + }); - runs(() => fs.writeFileSync(path.join(configDirPath, 'snippets.json'), "")); + runs(() => fs.writeFileSync(path.join(configDirPath, 'snippets.json'), "")); - return waitsFor("snippets to be removed", () => !snippetsService.snippetsForScopes(['.foo'])['foo']); - })); -}); + waitsFor("snippets to be removed", () => !snippetsService.snippetsForScopes(['.foo'])['foo']); + }); + }); + }); - describe("when ~/.atom/snippets.cson exists", function() { - beforeEach(function() { + describe("when ~/.atom/snippets.cson exists", () => { + beforeEach(() => { fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), `\ ".foo": "foo snippet": @@ -186,103 +185,108 @@ describe("Snippet Loading", function() { "body": "bar1"\ ` ); - return activateSnippetsPackage(); + activateSnippetsPackage(); }); - it("loads the snippets from that file", function() { + it("loads the snippets from that file", () => { let snippet = null; waitsFor(() => snippet = snippetsService.snippetsForScopes(['.foo'])['foo']); - return runs(function() { + runs(() => { expect(snippet.name).toBe('foo snippet'); expect(snippet.prefix).toBe("foo"); - return expect(snippet.body).toBe("bar1"); + expect(snippet.body).toBe("bar1"); }); }); - return describe("when that file changes", () => it("reloads the snippets", function() { - fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), `\ + describe("when that file changes", () => { + it("reloads the snippets", () => { + fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), `\ ".foo": -"foo snippet": - "prefix": "foo" - "body": "bar2"\ + "foo snippet": + "prefix": "foo" + "body": "bar2"\ ` - ); + ); - waitsFor("snippets to be changed", function() { - const snippet = snippetsService.snippetsForScopes(['.foo'])['foo']; - return (snippet != null ? snippet.body : undefined) === 'bar2'; - }); + waitsFor("snippets to be changed", () => { + const snippet = snippetsService.snippetsForScopes(['.foo'])['foo']; + return snippet && snippet.body === 'bar2'; + }); - runs(() => fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), "")); + runs(() => { + fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), ""); + }); - return waitsFor("snippets to be removed", function() { - const snippet = snippetsService.snippetsForScopes(['.foo'])['foo']; - return (snippet == null); + waitsFor("snippets to be removed", () => { + const snippet = snippetsService.snippetsForScopes(['.foo'])['foo']; + return snippet == null; + }); }); - })); + }); }); - it("notifies the user when the user snippets file cannot be loaded", function() { - fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), `\ -".junk":::\ -` - ); + it("notifies the user when the user snippets file cannot be loaded", () => { + fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), '".junk":::'); activateSnippetsPackage(); - return runs(function() { + runs(() => { expect(console.warn).toHaveBeenCalled(); - if (atom.notifications != null) { return expect(atom.notifications.addError).toHaveBeenCalled(); } + if (atom.notifications != null) { + expect(atom.notifications.addError).toHaveBeenCalled(); + } }); }); - return describe("packages-with-snippets-disabled feature", function() { - it("disables no snippets if the config option is empty", function() { + describe("packages-with-snippets-disabled feature", () => { + it("disables no snippets if the config option is empty", () => { const originalConfig = atom.config.get('core.packagesWithSnippetsDisabled'); atom.config.set('core.packagesWithSnippetsDisabled', []); activateSnippetsPackage(); - return runs(function() { + runs(() => { const snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']); expect(Object.keys(snippets).length).toBe(1); - return atom.config.set('core.packagesWithSnippetsDisabled', originalConfig); + atom.config.set('core.packagesWithSnippetsDisabled', originalConfig); }); }); - it("still includes a disabled package's snippets in the list of unparsed snippets", function() { + it("still includes a disabled package's snippets in the list of unparsed snippets", () => { let originalConfig = atom.config.get('core.packagesWithSnippetsDisabled'); atom.config.set('core.packagesWithSnippetsDisabled', []); activateSnippetsPackage(); - return runs(function() { + runs(() => { atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets']); const allSnippets = snippetsService.getUnparsedSnippets(); const scopedSnippet = allSnippets.find(s => s.selectorString === '.package-with-snippets-unique-scope'); expect(scopedSnippet).not.toBe(undefined); - return originalConfig = atom.config.get('core.packagesWithSnippetsDisabled'); + + //TODO: This doesn't look right. originalConfig should be restored + originalConfig = atom.config.get('core.packagesWithSnippetsDisabled'); }); }); - it("never loads a package's snippets when that package is disabled in config", function() { + it("never loads a package's snippets when that package is disabled in config", () => { const originalConfig = atom.config.get('core.packagesWithSnippetsDisabled'); atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets']); activateSnippetsPackage(); - return runs(function() { + runs(() => { const snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']); expect(Object.keys(snippets).length).toBe(0); - return atom.config.set('core.packagesWithSnippetsDisabled', originalConfig); + atom.config.set('core.packagesWithSnippetsDisabled', originalConfig); }); }); - return it("unloads and/or reloads snippets from a package if the config option is changed after activation", function() { + it("unloads and/or reloads snippets from a package if the config option is changed after activation", () => { const originalConfig = atom.config.get('core.packagesWithSnippetsDisabled'); atom.config.set('core.packagesWithSnippetsDisabled', []); activateSnippetsPackage(); - return runs(function() { + runs(() => { let snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']); expect(Object.keys(snippets).length).toBe(1); @@ -296,7 +300,7 @@ describe("Snippet Loading", function() { snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']); expect(Object.keys(snippets).length).toBe(1); - return atom.config.set('core.packagesWithSnippetsDisabled', originalConfig); + atom.config.set('core.packagesWithSnippetsDisabled', originalConfig); }); }); }); diff --git a/spec/snippets-spec.coffee b/spec/snippets-spec.coffee index dd00a5d..6e9b88b 100644 --- a/spec/snippets-spec.coffee +++ b/spec/snippets-spec.coffee @@ -1,1072 +1,1072 @@ -path = require 'path' -temp = require('temp').track() -Snippets = require '../lib/snippets' -{TextEditor} = require 'atom' - -describe "Snippets extension", -> - [editorElement, editor] = [] - - simulateTabKeyEvent = ({shift}={}) -> - event = atom.keymaps.constructor.buildKeydownEvent('tab', {shift, target: editorElement}) - atom.keymaps.handleKeyboardEvent(event) - - beforeEach -> - spyOn(Snippets, 'loadAll') - spyOn(Snippets, 'getUserSnippetsPath').andReturn('') - - waitsForPromise -> - atom.workspace.open('sample.js') - - waitsForPromise -> - atom.packages.activatePackage('language-javascript') - - waitsForPromise -> - atom.packages.activatePackage('snippets') - - runs -> - editor = atom.workspace.getActiveTextEditor() - editorElement = atom.views.getView(editor) - - afterEach -> - waitsForPromise -> - atom.packages.deactivatePackage('snippets') - - describe "provideSnippets interface", -> - snippetsInterface = null - - beforeEach -> - snippetsInterface = Snippets.provideSnippets() - - describe "bundledSnippetsLoaded", -> - it "indicates the loaded state of the bundled snippets", -> - expect(snippetsInterface.bundledSnippetsLoaded()).toBe false - Snippets.doneLoading() - expect(snippetsInterface.bundledSnippetsLoaded()).toBe true - - it "resets the loaded state after snippets is deactivated", -> - expect(snippetsInterface.bundledSnippetsLoaded()).toBe false - Snippets.doneLoading() - expect(snippetsInterface.bundledSnippetsLoaded()).toBe true - - waitsForPromise -> atom.packages.deactivatePackage('snippets') - waitsForPromise -> atom.packages.activatePackage('snippets') - - runs -> - expect(snippetsInterface.bundledSnippetsLoaded()).toBe false - Snippets.doneLoading() - expect(snippetsInterface.bundledSnippetsLoaded()).toBe true - - describe "insertSnippet", -> - it "can insert a snippet", -> - editor.setSelectedBufferRange([[0, 4], [0, 13]]) - snippetsInterface.insertSnippet("hello ${1:world}", editor) - expect(editor.lineTextForBufferRow(0)).toBe "var hello world = function () {" - - it "returns false for snippetToExpandUnderCursor if getSnippets returns {}", -> - snippets = atom.packages.getActivePackage('snippets').mainModule - expect(snippets.snippetToExpandUnderCursor(editor)).toEqual false - - it "ignores invalid snippets in the config", -> - snippets = atom.packages.getActivePackage('snippets').mainModule - - invalidSnippets = null - spyOn(snippets.scopedPropertyStore, 'getPropertyValue').andCallFake -> invalidSnippets - expect(snippets.getSnippets(editor)).toEqual {} - - invalidSnippets = 'test' - expect(snippets.getSnippets(editor)).toEqual {} - - invalidSnippets = [] - expect(snippets.getSnippets(editor)).toEqual {} - - invalidSnippets = 3 - expect(snippets.getSnippets(editor)).toEqual {} - - invalidSnippets = {a: null} - expect(snippets.getSnippets(editor)).toEqual {} - - describe "when null snippets are present", -> - beforeEach -> - Snippets.add __filename, - '.source.js': - "some snippet": - prefix: "t1" - body: "this is a test" - - '.source.js .nope': - "some snippet": - prefix: "t1" - body: null - - it "overrides the less-specific defined snippet", -> - snippets = Snippets.provideSnippets() - expect(snippets.snippetsForScopes(['.source.js'])['t1']).toBeTruthy() - expect(snippets.snippetsForScopes(['.source.js .nope.not-today'])['t1']).toBeFalsy() - - describe "when 'tab' is triggered on the editor", -> - beforeEach -> - Snippets.add __filename, - ".source.js": - "without tab stops": - prefix: "t1" - body: "this is a test" - - "with only an end tab stop": - prefix: "t1a" - body: "something $0 strange" - - "overlapping prefix": - prefix: "tt1" - body: "this is another test" - - "special chars": - prefix: "@unique" - body: "@unique see" - - "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 - $2 - """ - - "multiline with indented placeholder tabstop": - prefix: "t4" - body: """ - line ${1:1} - ${2:body...} - """ - - "multiline starting with tabstop": - prefix: "t4b" - body: """ - $1 = line 1 { - line 2 - } - """ - - "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} - """ - - "tab stops at beginning and then end of snippet": - prefix: "t6b" - body: "$1expanded$0" - - "tab stops at end and then beginning of snippet": - prefix: "t6c" - body: "$0expanded$1" - - "contains empty lines": - prefix: "t7" - body: """ - first line $1 - - - fourth line after blanks $2 - """ - "with/without placeholder": - prefix: "t8" - body: """ - with placeholder ${1:test} - without placeholder ${2} - """ - - "multi-caret": - prefix: "t9" - body: """ - with placeholder ${1:test} - without placeholder $1 - """ - - "multi-caret-multi-tabstop": - prefix: "t9b" - body: """ - with placeholder ${1:test} - without placeholder $1 - second tabstop $2 - third tabstop $3 - """ - - "large indices": - prefix: "t10" - body: """ - hello${10} ${11:large} indices${1} - """ - - "no body": - prefix: "bad1" - - "number body": - prefix: "bad2" - body: 100 - - "many tabstops": - prefix: "t11" - body: """ - $0one${1} ${2:two} three${3} - """ - - "simple transform": - prefix: "t12" - body: """ - [${1:b}][/${1/[ ]+.*$//}] - """ - "transform with non-transforming mirrors": - prefix: "t13" - body: """ - ${1:placeholder}\n${1/(.)/\\u$1/}\n$1 - """ - "multiple tab stops, some with transforms and some without": - prefix: "t14" - body: """ - ${1:placeholder} ${1/(.)/\\u$1/} $1 ${2:ANOTHER} ${2/^(.*)$/\\L$1/} $2 - """ - "has a transformed tab stop without a corresponding ordinary tab stop": - prefix: 't15' - body: """ - ${1/(.)/\\u$1/} & $2 - """ - "has a transformed tab stop that occurs before the corresponding ordinary tab stop": - prefix: 't16' - body: """ - & ${1/(.)/\\u$1/} & ${1:q} - """ - "has a placeholder that mirrors another tab stop's content": - prefix: 't17' - body: "$4console.${3:log}('${2:uh $1}', $1);$0" - "has a transformed tab stop such that it is possible to move the cursor between the ordinary tab stop and its transformed version without an intermediate step": - prefix: 't18' - body: '// $1\n// ${1/./=/}' - - it "parses snippets once, reusing cached ones on subsequent queries", -> - spyOn(Snippets, "getBodyParser").andCallThrough() - - editor.insertText("t1") - simulateTabKeyEvent() - - expect(Snippets.getBodyParser).toHaveBeenCalled() - expect(editor.lineTextForBufferRow(0)).toBe "this is a testvar quicksort = function () {" - expect(editor.getCursorScreenPosition()).toEqual [0, 14] - - Snippets.getBodyParser.reset() - - editor.setText("") - editor.insertText("t1") - simulateTabKeyEvent() - - expect(Snippets.getBodyParser).not.toHaveBeenCalled() - expect(editor.lineTextForBufferRow(0)).toBe "this is a test" - expect(editor.getCursorScreenPosition()).toEqual [0, 14] - - Snippets.getBodyParser.reset() - - Snippets.add __filename, - ".source.js": - "invalidate previous snippet": - prefix: "t1" - body: "new snippet" - - editor.setText("") - editor.insertText("t1") - simulateTabKeyEvent() - - expect(Snippets.getBodyParser).toHaveBeenCalled() - expect(editor.lineTextForBufferRow(0)).toBe "new snippet" - expect(editor.getCursorScreenPosition()).toEqual [0, 11] - - describe "when the snippet body is invalid or missing", -> - it "does not register the snippet", -> - editor.setText('') - editor.insertText('bad1') - atom.commands.dispatch editorElement, 'snippets:expand' - expect(editor.getText()).toBe 'bad1' - - editor.setText('') - editor.setText('bad2') - atom.commands.dispatch editorElement, 'snippets:expand' - expect(editor.getText()).toBe 'bad2' - - 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] - - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(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") - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "this is a testvar quicksort = function () {" - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(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.getMarkerCount() - editor.setCursorScreenPosition([2, 0]) - editor.insertText('t2') - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(2)).toBe "go here next:() and finally go here:()" - expect(editor.lineTextForBufferRow(3)).toBe "go here first:()" - expect(editor.lineTextForBufferRow(4)).toBe " if (items.length <= 1) return items;" - expect(editor.getSelectedBufferRange()).toEqual [[3, 15], [3, 15]] - - simulateTabKeyEvent() - expect(editor.getSelectedBufferRange()).toEqual [[2, 14], [2, 14]] - editor.insertText 'abc' - - simulateTabKeyEvent() - expect(editor.getSelectedBufferRange()).toEqual [[2, 40], [2, 40]] - - # tab backwards - simulateTabKeyEvent(shift: true) - expect(editor.getSelectedBufferRange()).toEqual [[2, 14], [2, 17]] # should highlight text typed at tab stop - - simulateTabKeyEvent(shift: true) - expect(editor.getSelectedBufferRange()).toEqual [[3, 15], [3, 15]] - - # shift-tab on first tab-stop does nothing - simulateTabKeyEvent(shift: true) - expect(editor.getCursorScreenPosition()).toEqual [3, 15] - - # tab through all tab stops, then tab on last stop to terminate snippet - simulateTabKeyEvent() - simulateTabKeyEvent() - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(2)).toBe "go here next:(abc) and finally go here:( )" - expect(editor.getMarkerCount()).toBe markerCountBefore - - describe "when tab stops are nested", -> - it "destroys the inner tab stop if the outer tab stop is modified", -> - editor.setText('') - editor.insertText 't5' - atom.commands.dispatch editorElement, 'snippets:expand' - expect(editor.lineTextForBufferRow(0)).toBe '"key": value' - expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 5]] - editor.insertText("foo") - simulateTabKeyEvent() - expect(editor.getSelectedBufferRange()).toEqual [[0, 5], [0, 10]] - - describe "when the only tab stop is an end stop", -> - it "terminates the snippet immediately after moving the cursor to the end stop", -> - editor.setText('') - editor.insertText 't1a' - simulateTabKeyEvent() - - expect(editor.lineTextForBufferRow(0)).toBe "something strange" - expect(editor.getCursorBufferPosition()).toEqual [0, 10] - - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "something strange" - expect(editor.getCursorBufferPosition()).toEqual [0, 12] - - describe "when tab stops are separated by blank lines", -> - it "correctly places the tab stops (regression)", -> - editor.setText('') - editor.insertText 't7' - atom.commands.dispatch editorElement, 'snippets:expand' - atom.commands.dispatch editorElement, 'snippets:next-tab-stop' - expect(editor.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') - simulateTabKeyEvent() - - editor.moveUp() - editor.moveLeft() - simulateTabKeyEvent() - - expect(editor.lineTextForBufferRow(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') - simulateTabKeyEvent() - simulateTabKeyEvent() - - editor.moveRight() - simulateTabKeyEvent(shift: true) - expect(editor.getCursorBufferPosition()).toEqual [4, 15] - - describe "when the cursor is moved within the bounds of the current tab stop", -> - it "should not terminate the snippet", -> - editor.setCursorScreenPosition([0, 0]) - editor.insertText('t8') - simulateTabKeyEvent() - - expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" - editor.moveRight() - editor.moveLeft() - editor.insertText("foo") - expect(editor.lineTextForBufferRow(0)).toBe "with placeholder tesfoot" - - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" - editor.insertText("test") - expect(editor.lineTextForBufferRow(1)).toBe "without placeholder testvar quicksort = function () {" - editor.moveLeft() - editor.insertText("foo") - expect(editor.lineTextForBufferRow(1)).toBe "without placeholder tesfootvar quicksort = function () {" - - describe "when the backspace is press within the bounds of the current tab stop", -> - it "should not terminate the snippet", -> - editor.setCursorScreenPosition([0, 0]) - editor.insertText('t8') - simulateTabKeyEvent() - - expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" - editor.moveRight() - editor.backspace() - editor.insertText("foo") - expect(editor.lineTextForBufferRow(0)).toBe "with placeholder tesfoo" - - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" - editor.insertText("test") - expect(editor.lineTextForBufferRow(1)).toBe "without placeholder testvar quicksort = function () {" - editor.backspace() - editor.insertText("foo") - expect(editor.lineTextForBufferRow(1)).toBe "without placeholder tesfoovar quicksort = function () {" - - 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(editor.getSoftTabs()).toBeTruthy() - editor.insertText("t3") - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(1)).toBe " line 2" - expect(editor.getCursorBufferPosition()).toEqual [1, 8] - - describe "when the edit session is in hard-tabs mode", -> - it "inserts hard tabs in the snippet directly", -> - editor.setSoftTabs(false) - editor.insertText("t3") - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(1)).toBe "\tline 2" - expect(editor.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' - atom.commands.dispatch editorElement, 'snippets:expand' - expect(editor.lineTextForBufferRow(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(editor.getSoftTabs()).toBeTruthy() - editor.setCursorScreenPosition([2, Infinity]) - editor.insertText ' t3' - atom.commands.dispatch editorElement, 'snippets:expand' - expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items; line 1" - expect(editor.lineTextForBufferRow(3)).toBe " line 2" - expect(editor.getCursorBufferPosition()).toEqual [3, 12] - - describe "when the snippet spans multiple lines", -> - beforeEach -> - editor.update({autoIndent: true}) - # editor.update() returns a Promise that never gets resolved, so we - # need to return undefined to avoid a timeout in the spec. - # TODO: Figure out why `editor.update({autoIndent: true})` never gets resolved. - return - - it "places tab stops correctly", -> - expect(editor.getSoftTabs()).toBeTruthy() - editor.setCursorScreenPosition([2, Infinity]) - editor.insertText ' t3' - atom.commands.dispatch editorElement, 'snippets:expand' - expect(editor.getCursorBufferPosition()).toEqual [3, 12] - atom.commands.dispatch editorElement, 'snippets:next-tab-stop' - expect(editor.getCursorBufferPosition()).toEqual [4, 4] - - it "indents the subsequent lines of the snippet based on the indent level before the snippet is inserted", -> - editor.setCursorScreenPosition([2, Infinity]) - editor.insertNewline() - editor.insertText 't4b' - atom.commands.dispatch editorElement, 'snippets:expand' - - expect(editor.lineTextForBufferRow(3)).toBe " = line 1 {" # 4 + 1 spaces (because the tab stop is invisible) - expect(editor.lineTextForBufferRow(4)).toBe " line 2" - expect(editor.lineTextForBufferRow(5)).toBe " }" - expect(editor.getCursorBufferPosition()).toEqual [3, 4] - - it "does not change the relative positioning of the tab stops when inserted multiple times", -> - editor.setCursorScreenPosition([2, Infinity]) - editor.insertNewline() - editor.insertText 't4' - atom.commands.dispatch editorElement, 'snippets:expand' - - expect(editor.getSelectedBufferRange()).toEqual [[3, 9], [3, 10]] - atom.commands.dispatch editorElement, 'snippets:next-tab-stop' - expect(editor.getSelectedBufferRange()).toEqual [[4, 6], [4, 13]] - - editor.insertText 't4' - atom.commands.dispatch editorElement, 'snippets:expand' - - expect(editor.getSelectedBufferRange()).toEqual [[4, 11], [4, 12]] - atom.commands.dispatch editorElement, 'snippets:next-tab-stop' - expect(editor.getSelectedBufferRange()).toEqual [[5, 8], [5, 15]] - - editor.setText('') # Clear editor - editor.insertText 't4' - atom.commands.dispatch editorElement, 'snippets:expand' - - expect(editor.getSelectedBufferRange()).toEqual [[0, 5], [0, 6]] - atom.commands.dispatch editorElement, 'snippets:next-tab-stop' - expect(editor.getSelectedBufferRange()).toEqual [[1, 2], [1, 9]] - - describe "when multiple snippets match the prefix", -> - it "expands the snippet that is the longest match for the prefix", -> - editor.insertText('t113') - expect(editor.getCursorScreenPosition()).toEqual [0, 4] - - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "t113 var quicksort = function () {" - expect(editor.getCursorScreenPosition()).toEqual [0, 6] - - editor.undo() - editor.undo() - - editor.insertText("tt1") - expect(editor.getCursorScreenPosition()).toEqual [0, 3] - - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "this is another testvar quicksort = function () {" - expect(editor.getCursorScreenPosition()).toEqual [0, 20] - - editor.undo() - editor.undo() - - editor.insertText("@t1") - expect(editor.getCursorScreenPosition()).toEqual [0, 3] - - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "@this is a testvar quicksort = function () {" - expect(editor.getCursorScreenPosition()).toEqual [0, 15] - - describe "when the word preceding the cursor ends with a snippet prefix", -> - it "inserts a tab as normal", -> - editor.insertText("t1t1t1") - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "t1t1t1 var quicksort = function () {" - - describe "when the letters preceding the cursor don't match a snippet", -> - it "inserts a tab as normal", -> - editor.insertText("xxte") - expect(editor.getCursorScreenPosition()).toEqual [0, 4] - - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "xxte var quicksort = function () {" - expect(editor.getCursorScreenPosition()).toEqual [0, 6] - - describe "when text is selected", -> - it "inserts a tab as normal", -> - editor.insertText("t1") - editor.setSelectedBufferRange([[0, 0], [0, 2]]) - - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe " t1var quicksort = function () {" - expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 4]] - - describe "when a previous snippet expansion has just been undone", -> - describe "when the tab stops appear in the middle of the snippet", -> - 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] - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "first line" - editor.undo() - expect(editor.lineTextForBufferRow(0)).toBe "t6" - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "first line" - - describe "when the tab stops appear at the beginning and then the end of snippet", -> - it "expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", -> - editor.insertText 't6b\n' - editor.setCursorBufferPosition [0, 3] - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "expanded" - editor.undo() - expect(editor.lineTextForBufferRow(0)).toBe "t6b" - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "expanded" - expect(editor.getCursorBufferPosition()).toEqual([0, 0]) - - describe "when the tab stops appear at the end and then the beginning of snippet", -> - it "expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", -> - editor.insertText 't6c\n' - editor.setCursorBufferPosition [0, 3] - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "expanded" - editor.undo() - expect(editor.lineTextForBufferRow(0)).toBe "t6c" - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "expanded" - expect(editor.getCursorBufferPosition()).toEqual([0, 8]) - - describe "when the prefix contains non-word characters", -> - it "selects the non-word characters as part of the prefix", -> - editor.insertText("@unique") - expect(editor.getCursorScreenPosition()).toEqual [0, 7] - - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "@unique seevar quicksort = function () {" - expect(editor.getCursorScreenPosition()).toEqual [0, 11] - - editor.setCursorBufferPosition [10, 0] - editor.insertText("'@unique") - - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(10)).toBe "'@unique see" - expect(editor.getCursorScreenPosition()).toEqual [10, 12] - - it "does not select the whitespace before the prefix", -> - editor.insertText("a; @unique") - expect(editor.getCursorScreenPosition()).toEqual [0, 10] - - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "a; @unique seevar quicksort = function () {" - expect(editor.getCursorScreenPosition()).toEqual [0, 14] - - describe "when snippet contains tabstops with or without placeholder", -> - it "should create two markers", -> - editor.setCursorScreenPosition([0, 0]) - editor.insertText('t8') - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" - expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" - - expect(editor.getSelectedBufferRange()).toEqual [[0, 17], [0, 21]] - - simulateTabKeyEvent() - expect(editor.getSelectedBufferRange()).toEqual [[1, 20], [1, 20]] - - describe "when snippet contains multi-caret tabstops with or without placeholder", -> - it "should create two markers", -> - editor.setCursorScreenPosition([0, 0]) - editor.insertText('t9') - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" - expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" - editor.insertText('hello') - expect(editor.lineTextForBufferRow(0)).toBe "with placeholder hello" - expect(editor.lineTextForBufferRow(1)).toBe "without placeholder hellovar quicksort = function () {" - - it "terminates the snippet when cursors are destroyed", -> - editor.setCursorScreenPosition([0, 0]) - editor.insertText('t9b') - simulateTabKeyEvent() - editor.getCursors()[0].destroy() - editor.getCursorBufferPosition() - simulateTabKeyEvent() - - expect(editor.lineTextForBufferRow(1)).toEqual("without placeholder ") - - it "terminates the snippet expansion if a new cursor moves outside the bounds of the tab stops", -> - editor.setCursorScreenPosition([0, 0]) - editor.insertText('t9b') - simulateTabKeyEvent() - editor.insertText('test') - - editor.getCursors()[0].destroy() - editor.moveDown() # this should destroy the previous expansion - editor.moveToBeginningOfLine() - - # this should insert whitespace instead of going through tabstops of the previous destroyed snippet - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(2).indexOf(" second")).toBe 0 - - it "moves to the second tabstop after a multi-caret tabstop", -> - editor.setCursorScreenPosition([0, 0]) - editor.insertText('t9b') - simulateTabKeyEvent() - editor.insertText('line 1') - - simulateTabKeyEvent() - editor.insertText('line 2') - - simulateTabKeyEvent() - editor.insertText('line 3') - - expect(editor.lineTextForBufferRow(2).indexOf("line 2 ")).toBe -1 - - it "mirrors input properly when a tabstop's placeholder refers to another tabstop", -> - editor.setText('t17') - editor.setCursorScreenPosition([0, 3]) - simulateTabKeyEvent() - editor.insertText("foo") - expect(editor.getText()).toBe "console.log('uh foo', foo);" - simulateTabKeyEvent() - editor.insertText("bar") - expect(editor.getText()).toBe "console.log('bar', foo);" - - describe "when the snippet contains tab stops with transformations", -> - it "transforms the text typed into the first tab stop before setting it in the transformed tab stop", -> - editor.setText('t12') - editor.setCursorScreenPosition([0, 3]) - simulateTabKeyEvent() - expect(editor.getText()).toBe("[b][/b]") - editor.insertText('img src') - expect(editor.getText()).toBe("[img src][/img]") - - it "bundles the transform mutations along with the original manual mutation for the purposes of undo and redo", -> - editor.setText('t12') - editor.setCursorScreenPosition([0, 3]) - simulateTabKeyEvent() - editor.insertText('i') - expect(editor.getText()).toBe("[i][/i]") - - editor.insertText('mg src') - expect(editor.getText()).toBe("[img src][/img]") - - editor.undo() - expect(editor.getText()).toBe("[i][/i]") - - editor.redo() - expect(editor.getText()).toBe("[img src][/img]") - - it "can pick the right insertion to use as the primary even if a transformed insertion occurs first in the snippet", -> - editor.setText('t16') - editor.setCursorScreenPosition([0, 3]) - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe("& Q & q") - expect(editor.getCursorBufferPosition()).toEqual([0, 7]) - - editor.insertText('rst') - expect(editor.lineTextForBufferRow(0)).toBe("& RST & rst") - - it "silently ignores a tab stop without a non-transformed insertion to use as the primary", -> - editor.setText('t15') - editor.setCursorScreenPosition([0, 3]) - simulateTabKeyEvent() - editor.insertText('a') - expect(editor.lineTextForBufferRow(0)).toBe(" & a") - expect(editor.getCursorBufferPosition()).toEqual([0, 4]) - - describe "when the snippet contains mirrored tab stops and tab stops with transformations", -> - it "adds cursors for the mirrors but not the transformations", -> - editor.setText('t13') - editor.setCursorScreenPosition([0, 3]) - simulateTabKeyEvent() - expect(editor.getCursors().length).toBe(2) - expect(editor.getText()).toBe """ - placeholder - PLACEHOLDER - - """ - - editor.insertText('foo') - - expect(editor.getText()).toBe """ - foo - FOO - foo - """ - - describe "when the snippet contains multiple tab stops, some with transformations and some without", -> - it "does not get confused", -> - editor.setText('t14') - editor.setCursorScreenPosition([0, 3]) - simulateTabKeyEvent() - expect(editor.getCursors().length).toBe(2) - expect(editor.getText()).toBe "placeholder PLACEHOLDER ANOTHER another " - simulateTabKeyEvent() - expect(editor.getCursors().length).toBe(2) - editor.insertText('FOO') - expect(editor.getText()).toBe """ - placeholder PLACEHOLDER FOO foo FOO - """ - - describe "when the snippet has a transformed tab stop such that it is possible to move the cursor between the ordinary tab stop and its transformed version without an intermediate step", -> - it "terminates the snippet upon such a cursor move", -> - editor.setText('t18') - editor.setCursorScreenPosition([0, 3]) - simulateTabKeyEvent() - expect(editor.getText()).toBe("// \n// ") - expect(editor.getCursorBufferPosition()).toEqual [0, 3] - editor.insertText('wat') - expect(editor.getText()).toBe("// wat\n// ===") - # Move the cursor down one line, then up one line. This puts the cursor - # back in its previous position, but the snippet should no longer be - # active, so when we type more text, it should not be mirrored. - editor.setCursorScreenPosition([1, 6]) - editor.setCursorScreenPosition([0, 6]) - editor.insertText('wat') - expect(editor.getText()).toBe("// watwat\n// ===") - - - describe "when the snippet contains tab stops with an index >= 10", -> - it "parses and orders the indices correctly", -> - editor.setText('t10') - editor.setCursorScreenPosition([0, 3]) - simulateTabKeyEvent() - expect(editor.getText()).toBe "hello large indices" - expect(editor.getCursorBufferPosition()).toEqual [0, 19] - simulateTabKeyEvent() - expect(editor.getCursorBufferPosition()).toEqual [0, 5] - simulateTabKeyEvent() - expect(editor.getSelectedBufferRange()).toEqual [[0, 6], [0, 11]] - - describe "when there are multiple cursors", -> - describe "when the cursors share a common snippet prefix", -> - it "expands the snippet for all cursors and allows simultaneous editing", -> - editor.insertText('t9') - editor.setCursorBufferPosition([12, 2]) - editor.insertText(' t9') - editor.addCursorAtBufferPosition([0, 2]) - simulateTabKeyEvent() - - expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" - expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" - expect(editor.lineTextForBufferRow(13)).toBe "}; with placeholder test" - expect(editor.lineTextForBufferRow(14)).toBe "without placeholder " - - editor.insertText('hello') - expect(editor.lineTextForBufferRow(0)).toBe "with placeholder hello" - expect(editor.lineTextForBufferRow(1)).toBe "without placeholder hellovar quicksort = function () {" - expect(editor.lineTextForBufferRow(13)).toBe "}; with placeholder hello" - expect(editor.lineTextForBufferRow(14)).toBe "without placeholder hello" - - it "applies transformations identically to single-expansion mode", -> - editor.setText('t14\nt14') - editor.setCursorBufferPosition([1, 3]) - editor.addCursorAtBufferPosition([0, 3]) - simulateTabKeyEvent() - - expect(editor.lineTextForBufferRow(0)).toBe "placeholder PLACEHOLDER ANOTHER another " - expect(editor.lineTextForBufferRow(1)).toBe "placeholder PLACEHOLDER ANOTHER another " - - editor.insertText "testing" - - expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing ANOTHER another " - expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing ANOTHER another " - - simulateTabKeyEvent() - editor.insertText "AGAIN" - - expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing AGAIN again AGAIN" - expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing AGAIN again AGAIN" - - it "bundles transform-induced mutations into a single history entry along with their triggering edit, even across multiple snippets", -> - editor.setText('t14\nt14') - editor.setCursorBufferPosition([1, 3]) - editor.addCursorAtBufferPosition([0, 3]) - simulateTabKeyEvent() - - expect(editor.lineTextForBufferRow(0)).toBe "placeholder PLACEHOLDER ANOTHER another " - expect(editor.lineTextForBufferRow(1)).toBe "placeholder PLACEHOLDER ANOTHER another " - - editor.insertText "testing" - - expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing ANOTHER another " - expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing ANOTHER another " - - simulateTabKeyEvent() - editor.insertText "AGAIN" - - expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing AGAIN again AGAIN" - expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing AGAIN again AGAIN" - - editor.undo() - expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing ANOTHER another " - expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing ANOTHER another " - - editor.undo() - expect(editor.lineTextForBufferRow(0)).toBe "placeholder PLACEHOLDER ANOTHER another " - expect(editor.lineTextForBufferRow(1)).toBe "placeholder PLACEHOLDER ANOTHER another " - - editor.redo() - expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing ANOTHER another " - expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing ANOTHER another " - - editor.redo() - expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing AGAIN again AGAIN" - expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing AGAIN again AGAIN" - - describe "when there are many tabstops", -> - it "moves the cursors between the tab stops for their corresponding snippet when tab and shift-tab are pressed", -> - editor.addCursorAtBufferPosition([7, 5]) - editor.addCursorAtBufferPosition([12, 2]) - editor.insertText('t11') - simulateTabKeyEvent() - - cursors = editor.getCursors() - expect(cursors.length).toEqual 3 - - expect(cursors[0].getBufferPosition()).toEqual [0, 3] - expect(cursors[1].getBufferPosition()).toEqual [7, 8] - expect(cursors[2].getBufferPosition()).toEqual [12, 5] - expect(cursors[0].selection.isEmpty()).toBe true - expect(cursors[1].selection.isEmpty()).toBe true - expect(cursors[2].selection.isEmpty()).toBe true - - simulateTabKeyEvent() - expect(cursors[0].getBufferPosition()).toEqual [0, 7] - expect(cursors[1].getBufferPosition()).toEqual [7, 12] - expect(cursors[2].getBufferPosition()).toEqual [12, 9] - expect(cursors[0].selection.isEmpty()).toBe false - expect(cursors[1].selection.isEmpty()).toBe false - expect(cursors[2].selection.isEmpty()).toBe false - expect(cursors[0].selection.getText()).toEqual 'two' - expect(cursors[1].selection.getText()).toEqual 'two' - expect(cursors[2].selection.getText()).toEqual 'two' - - simulateTabKeyEvent() - expect(cursors[0].getBufferPosition()).toEqual [0, 13] - expect(cursors[1].getBufferPosition()).toEqual [7, 18] - expect(cursors[2].getBufferPosition()).toEqual [12, 15] - expect(cursors[0].selection.isEmpty()).toBe true - expect(cursors[1].selection.isEmpty()).toBe true - expect(cursors[2].selection.isEmpty()).toBe true - - simulateTabKeyEvent() - expect(cursors[0].getBufferPosition()).toEqual [0, 0] - expect(cursors[1].getBufferPosition()).toEqual [7, 5] - expect(cursors[2].getBufferPosition()).toEqual [12, 2] - expect(cursors[0].selection.isEmpty()).toBe true - expect(cursors[1].selection.isEmpty()).toBe true - expect(cursors[2].selection.isEmpty()).toBe true - - describe "when the cursors do not share common snippet prefixes", -> - it "inserts tabs as normal", -> - editor.insertText('t9') - editor.setCursorBufferPosition([12, 2]) - editor.insertText(' t8') - editor.addCursorAtBufferPosition([0, 2]) - simulateTabKeyEvent() - expect(editor.lineTextForBufferRow(0)).toBe "t9 var quicksort = function () {" - expect(editor.lineTextForBufferRow(12)).toBe "}; t8 " - - describe "when a snippet is triggered within an existing snippet expansion", -> - it "ignores the snippet expansion and goes to the next tab stop", -> - editor.addCursorAtBufferPosition([7, 5]) - editor.addCursorAtBufferPosition([12, 2]) - editor.insertText('t11') - simulateTabKeyEvent() - simulateTabKeyEvent() - - editor.insertText('t1') - simulateTabKeyEvent() - - cursors = editor.getCursors() - expect(cursors.length).toEqual 3 - - expect(cursors[0].getBufferPosition()).toEqual [0, 12] - expect(cursors[1].getBufferPosition()).toEqual [7, 17] - expect(cursors[2].getBufferPosition()).toEqual [12, 14] - expect(cursors[0].selection.isEmpty()).toBe true - expect(cursors[1].selection.isEmpty()).toBe true - expect(cursors[2].selection.isEmpty()).toBe true - expect(editor.lineTextForBufferRow(0)).toBe "one t1 threevar quicksort = function () {" - expect(editor.lineTextForBufferRow(7)).toBe " }one t1 three" - expect(editor.lineTextForBufferRow(12)).toBe "};one t1 three" - - describe "when the editor is not a pane item (regression)", -> - it "handles tab stops correctly", -> - editor = new TextEditor() - atom.grammars.assignLanguageMode(editor, 'source.js') - editorElement = editor.getElement() - - editor.insertText('t2') - simulateTabKeyEvent() - editor.insertText('ABC') - expect(editor.getText()).toContain('go here first:(ABC)') - - editor.undo() - editor.undo() - expect(editor.getText()).toBe('t2') - simulateTabKeyEvent() - editor.insertText('ABC') - expect(editor.getText()).toContain('go here first:(ABC)') - - describe "when atom://.atom/snippets is opened", -> - it "opens ~/.atom/snippets.cson", -> - jasmine.unspy(Snippets, 'getUserSnippetsPath') - atom.workspace.destroyActivePaneItem() - configDirPath = temp.mkdirSync('atom-config-dir-') - spyOn(atom, 'getConfigDirPath').andReturn configDirPath - atom.workspace.open('atom://.atom/snippets') - - waitsFor -> - atom.workspace.getActiveTextEditor()? - - runs -> - expect(atom.workspace.getActiveTextEditor().getURI()).toBe path.join(configDirPath, 'snippets.cson') - - describe "snippet insertion API", -> - it "will automatically parse snippet definition and replace selection", -> - editor.setSelectedBufferRange([[0, 4], [0, 13]]) - Snippets.insert("hello ${1:world}", editor) - - expect(editor.lineTextForBufferRow(0)).toBe "var hello world = function () {" - expect(editor.getSelectedBufferRange()).toEqual [[0, 10], [0, 15]] - - describe "when the 'snippets:available' command is triggered", -> - availableSnippetsView = null - - beforeEach -> - Snippets.add __filename, - ".source.js": - "test": - prefix: "test" - body: "${1:Test pass you will}, young " - - "challenge": - prefix: "chal" - body: "$1: ${2:To pass this challenge}" - - delete Snippets.availableSnippetsView - - atom.commands.dispatch(editorElement, "snippets:available") - - waitsFor -> - atom.workspace.getModalPanels().length is 1 - - runs -> - availableSnippetsView = atom.workspace.getModalPanels()[0].getItem() - - it "renders a select list of all available snippets", -> - expect(availableSnippetsView.selectListView.getSelectedItem().prefix).toBe 'test' - expect(availableSnippetsView.selectListView.getSelectedItem().name).toBe 'test' - expect(availableSnippetsView.selectListView.getSelectedItem().bodyText).toBe '${1:Test pass you will}, young ' - - availableSnippetsView.selectListView.selectNext() - - expect(availableSnippetsView.selectListView.getSelectedItem().prefix).toBe 'chal' - expect(availableSnippetsView.selectListView.getSelectedItem().name).toBe 'challenge' - expect(availableSnippetsView.selectListView.getSelectedItem().bodyText).toBe '$1: ${2:To pass this challenge}' - - it "writes the selected snippet to the editor as snippet", -> - availableSnippetsView.selectListView.confirmSelection() - - expect(editor.getCursorScreenPosition()).toEqual [0, 18] - expect(editor.getSelectedText()).toBe 'Test pass you will' - expect(editor.lineTextForBufferRow(0)).toBe 'Test pass you will, young var quicksort = function () {' - - it "closes the dialog when triggered again", -> - atom.commands.dispatch availableSnippetsView.selectListView.refs.queryEditor.element, 'snippets:available' - expect(atom.workspace.getModalPanels().length).toBe 0 +# path = require 'path' +# temp = require('temp').track() +# Snippets = require '../lib/snippets' +# {TextEditor} = require 'atom' +# +# describe "Snippets extension", -> +# [editorElement, editor] = [] +# +# simulateTabKeyEvent = ({shift}={}) -> +# event = atom.keymaps.constructor.buildKeydownEvent('tab', {shift, target: editorElement}) +# atom.keymaps.handleKeyboardEvent(event) +# +# beforeEach -> +# spyOn(Snippets, 'loadAll') +# spyOn(Snippets, 'getUserSnippetsPath').andReturn('') +# +# waitsForPromise -> +# atom.workspace.open('sample.js') +# +# waitsForPromise -> +# atom.packages.activatePackage('language-javascript') +# +# waitsForPromise -> +# atom.packages.activatePackage('snippets') +# +# runs -> +# editor = atom.workspace.getActiveTextEditor() +# editorElement = atom.views.getView(editor) +# +# afterEach -> +# waitsForPromise -> +# atom.packages.deactivatePackage('snippets') +# +# describe "provideSnippets interface", -> +# snippetsInterface = null +# +# beforeEach -> +# snippetsInterface = Snippets.provideSnippets() +# +# describe "bundledSnippetsLoaded", -> +# it "indicates the loaded state of the bundled snippets", -> +# expect(snippetsInterface.bundledSnippetsLoaded()).toBe false +# Snippets.doneLoading() +# expect(snippetsInterface.bundledSnippetsLoaded()).toBe true +# +# it "resets the loaded state after snippets is deactivated", -> +# expect(snippetsInterface.bundledSnippetsLoaded()).toBe false +# Snippets.doneLoading() +# expect(snippetsInterface.bundledSnippetsLoaded()).toBe true +# +# waitsForPromise -> atom.packages.deactivatePackage('snippets') +# waitsForPromise -> atom.packages.activatePackage('snippets') +# +# runs -> +# expect(snippetsInterface.bundledSnippetsLoaded()).toBe false +# Snippets.doneLoading() +# expect(snippetsInterface.bundledSnippetsLoaded()).toBe true +# +# describe "insertSnippet", -> +# it "can insert a snippet", -> +# editor.setSelectedBufferRange([[0, 4], [0, 13]]) +# snippetsInterface.insertSnippet("hello ${1:world}", editor) +# expect(editor.lineTextForBufferRow(0)).toBe "var hello world = function () {" +# +# it "returns false for snippetToExpandUnderCursor if getSnippets returns {}", -> +# snippets = atom.packages.getActivePackage('snippets').mainModule +# expect(snippets.snippetToExpandUnderCursor(editor)).toEqual false +# +# it "ignores invalid snippets in the config", -> +# snippets = atom.packages.getActivePackage('snippets').mainModule +# +# invalidSnippets = null +# spyOn(snippets.scopedPropertyStore, 'getPropertyValue').andCallFake -> invalidSnippets +# expect(snippets.getSnippets(editor)).toEqual {} +# +# invalidSnippets = 'test' +# expect(snippets.getSnippets(editor)).toEqual {} +# +# invalidSnippets = [] +# expect(snippets.getSnippets(editor)).toEqual {} +# +# invalidSnippets = 3 +# expect(snippets.getSnippets(editor)).toEqual {} +# +# invalidSnippets = {a: null} +# expect(snippets.getSnippets(editor)).toEqual {} +# +# describe "when null snippets are present", -> +# beforeEach -> +# Snippets.add __filename, +# '.source.js': +# "some snippet": +# prefix: "t1" +# body: "this is a test" +# +# '.source.js .nope': +# "some snippet": +# prefix: "t1" +# body: null +# +# it "overrides the less-specific defined snippet", -> +# snippets = Snippets.provideSnippets() +# expect(snippets.snippetsForScopes(['.source.js'])['t1']).toBeTruthy() +# expect(snippets.snippetsForScopes(['.source.js .nope.not-today'])['t1']).toBeFalsy() +# +# describe "when 'tab' is triggered on the editor", -> +# beforeEach -> +# Snippets.add __filename, +# ".source.js": +# "without tab stops": +# prefix: "t1" +# body: "this is a test" +# +# "with only an end tab stop": +# prefix: "t1a" +# body: "something $0 strange" +# +# "overlapping prefix": +# prefix: "tt1" +# body: "this is another test" +# +# "special chars": +# prefix: "@unique" +# body: "@unique see" +# +# "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 +# $2 +# """ +# +# "multiline with indented placeholder tabstop": +# prefix: "t4" +# body: """ +# line ${1:1} +# ${2:body...} +# """ +# +# "multiline starting with tabstop": +# prefix: "t4b" +# body: """ +# $1 = line 1 { +# line 2 +# } +# """ +# +# "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} +# """ +# +# "tab stops at beginning and then end of snippet": +# prefix: "t6b" +# body: "$1expanded$0" +# +# "tab stops at end and then beginning of snippet": +# prefix: "t6c" +# body: "$0expanded$1" +# +# "contains empty lines": +# prefix: "t7" +# body: """ +# first line $1 +# +# +# fourth line after blanks $2 +# """ +# "with/without placeholder": +# prefix: "t8" +# body: """ +# with placeholder ${1:test} +# without placeholder ${2} +# """ +# +# "multi-caret": +# prefix: "t9" +# body: """ +# with placeholder ${1:test} +# without placeholder $1 +# """ +# +# "multi-caret-multi-tabstop": +# prefix: "t9b" +# body: """ +# with placeholder ${1:test} +# without placeholder $1 +# second tabstop $2 +# third tabstop $3 +# """ +# +# "large indices": +# prefix: "t10" +# body: """ +# hello${10} ${11:large} indices${1} +# """ +# +# "no body": +# prefix: "bad1" +# +# "number body": +# prefix: "bad2" +# body: 100 +# +# "many tabstops": +# prefix: "t11" +# body: """ +# $0one${1} ${2:two} three${3} +# """ +# +# "simple transform": +# prefix: "t12" +# body: """ +# [${1:b}][/${1/[ ]+.*$//}] +# """ +# "transform with non-transforming mirrors": +# prefix: "t13" +# body: """ +# ${1:placeholder}\n${1/(.)/\\u$1/}\n$1 +# """ +# "multiple tab stops, some with transforms and some without": +# prefix: "t14" +# body: """ +# ${1:placeholder} ${1/(.)/\\u$1/} $1 ${2:ANOTHER} ${2/^(.*)$/\\L$1/} $2 +# """ +# "has a transformed tab stop without a corresponding ordinary tab stop": +# prefix: 't15' +# body: """ +# ${1/(.)/\\u$1/} & $2 +# """ +# "has a transformed tab stop that occurs before the corresponding ordinary tab stop": +# prefix: 't16' +# body: """ +# & ${1/(.)/\\u$1/} & ${1:q} +# """ +# "has a placeholder that mirrors another tab stop's content": +# prefix: 't17' +# body: "$4console.${3:log}('${2:uh $1}', $1);$0" +# "has a transformed tab stop such that it is possible to move the cursor between the ordinary tab stop and its transformed version without an intermediate step": +# prefix: 't18' +# body: '// $1\n// ${1/./=/}' +# +# it "parses snippets once, reusing cached ones on subsequent queries", -> +# spyOn(Snippets, "getBodyParser").andCallThrough() +# +# editor.insertText("t1") +# simulateTabKeyEvent() +# +# expect(Snippets.getBodyParser).toHaveBeenCalled() +# expect(editor.lineTextForBufferRow(0)).toBe "this is a testvar quicksort = function () {" +# expect(editor.getCursorScreenPosition()).toEqual [0, 14] +# +# Snippets.getBodyParser.reset() +# +# editor.setText("") +# editor.insertText("t1") +# simulateTabKeyEvent() +# +# expect(Snippets.getBodyParser).not.toHaveBeenCalled() +# expect(editor.lineTextForBufferRow(0)).toBe "this is a test" +# expect(editor.getCursorScreenPosition()).toEqual [0, 14] +# +# Snippets.getBodyParser.reset() +# +# Snippets.add __filename, +# ".source.js": +# "invalidate previous snippet": +# prefix: "t1" +# body: "new snippet" +# +# editor.setText("") +# editor.insertText("t1") +# simulateTabKeyEvent() +# +# expect(Snippets.getBodyParser).toHaveBeenCalled() +# expect(editor.lineTextForBufferRow(0)).toBe "new snippet" +# expect(editor.getCursorScreenPosition()).toEqual [0, 11] +# +# describe "when the snippet body is invalid or missing", -> +# it "does not register the snippet", -> +# editor.setText('') +# editor.insertText('bad1') +# atom.commands.dispatch editorElement, 'snippets:expand' +# expect(editor.getText()).toBe 'bad1' +# +# editor.setText('') +# editor.setText('bad2') +# atom.commands.dispatch editorElement, 'snippets:expand' +# expect(editor.getText()).toBe 'bad2' +# +# 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] +# +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(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") +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "this is a testvar quicksort = function () {" +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(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.getMarkerCount() +# editor.setCursorScreenPosition([2, 0]) +# editor.insertText('t2') +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(2)).toBe "go here next:() and finally go here:()" +# expect(editor.lineTextForBufferRow(3)).toBe "go here first:()" +# expect(editor.lineTextForBufferRow(4)).toBe " if (items.length <= 1) return items;" +# expect(editor.getSelectedBufferRange()).toEqual [[3, 15], [3, 15]] +# +# simulateTabKeyEvent() +# expect(editor.getSelectedBufferRange()).toEqual [[2, 14], [2, 14]] +# editor.insertText 'abc' +# +# simulateTabKeyEvent() +# expect(editor.getSelectedBufferRange()).toEqual [[2, 40], [2, 40]] +# +# # tab backwards +# simulateTabKeyEvent(shift: true) +# expect(editor.getSelectedBufferRange()).toEqual [[2, 14], [2, 17]] # should highlight text typed at tab stop +# +# simulateTabKeyEvent(shift: true) +# expect(editor.getSelectedBufferRange()).toEqual [[3, 15], [3, 15]] +# +# # shift-tab on first tab-stop does nothing +# simulateTabKeyEvent(shift: true) +# expect(editor.getCursorScreenPosition()).toEqual [3, 15] +# +# # tab through all tab stops, then tab on last stop to terminate snippet +# simulateTabKeyEvent() +# simulateTabKeyEvent() +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(2)).toBe "go here next:(abc) and finally go here:( )" +# expect(editor.getMarkerCount()).toBe markerCountBefore +# +# describe "when tab stops are nested", -> +# it "destroys the inner tab stop if the outer tab stop is modified", -> +# editor.setText('') +# editor.insertText 't5' +# atom.commands.dispatch editorElement, 'snippets:expand' +# expect(editor.lineTextForBufferRow(0)).toBe '"key": value' +# expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 5]] +# editor.insertText("foo") +# simulateTabKeyEvent() +# expect(editor.getSelectedBufferRange()).toEqual [[0, 5], [0, 10]] +# +# describe "when the only tab stop is an end stop", -> +# it "terminates the snippet immediately after moving the cursor to the end stop", -> +# editor.setText('') +# editor.insertText 't1a' +# simulateTabKeyEvent() +# +# expect(editor.lineTextForBufferRow(0)).toBe "something strange" +# expect(editor.getCursorBufferPosition()).toEqual [0, 10] +# +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "something strange" +# expect(editor.getCursorBufferPosition()).toEqual [0, 12] +# +# describe "when tab stops are separated by blank lines", -> +# it "correctly places the tab stops (regression)", -> +# editor.setText('') +# editor.insertText 't7' +# atom.commands.dispatch editorElement, 'snippets:expand' +# atom.commands.dispatch editorElement, 'snippets:next-tab-stop' +# expect(editor.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') +# simulateTabKeyEvent() +# +# editor.moveUp() +# editor.moveLeft() +# simulateTabKeyEvent() +# +# expect(editor.lineTextForBufferRow(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') +# simulateTabKeyEvent() +# simulateTabKeyEvent() +# +# editor.moveRight() +# simulateTabKeyEvent(shift: true) +# expect(editor.getCursorBufferPosition()).toEqual [4, 15] +# +# describe "when the cursor is moved within the bounds of the current tab stop", -> +# it "should not terminate the snippet", -> +# editor.setCursorScreenPosition([0, 0]) +# editor.insertText('t8') +# simulateTabKeyEvent() +# +# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" +# editor.moveRight() +# editor.moveLeft() +# editor.insertText("foo") +# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder tesfoot" +# +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" +# editor.insertText("test") +# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder testvar quicksort = function () {" +# editor.moveLeft() +# editor.insertText("foo") +# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder tesfootvar quicksort = function () {" +# +# describe "when the backspace is press within the bounds of the current tab stop", -> +# it "should not terminate the snippet", -> +# editor.setCursorScreenPosition([0, 0]) +# editor.insertText('t8') +# simulateTabKeyEvent() +# +# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" +# editor.moveRight() +# editor.backspace() +# editor.insertText("foo") +# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder tesfoo" +# +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" +# editor.insertText("test") +# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder testvar quicksort = function () {" +# editor.backspace() +# editor.insertText("foo") +# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder tesfoovar quicksort = function () {" +# +# 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(editor.getSoftTabs()).toBeTruthy() +# editor.insertText("t3") +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(1)).toBe " line 2" +# expect(editor.getCursorBufferPosition()).toEqual [1, 8] +# +# describe "when the edit session is in hard-tabs mode", -> +# it "inserts hard tabs in the snippet directly", -> +# editor.setSoftTabs(false) +# editor.insertText("t3") +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(1)).toBe "\tline 2" +# expect(editor.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' +# atom.commands.dispatch editorElement, 'snippets:expand' +# expect(editor.lineTextForBufferRow(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(editor.getSoftTabs()).toBeTruthy() +# editor.setCursorScreenPosition([2, Infinity]) +# editor.insertText ' t3' +# atom.commands.dispatch editorElement, 'snippets:expand' +# expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items; line 1" +# expect(editor.lineTextForBufferRow(3)).toBe " line 2" +# expect(editor.getCursorBufferPosition()).toEqual [3, 12] +# +# describe "when the snippet spans multiple lines", -> +# beforeEach -> +# editor.update({autoIndent: true}) +# # editor.update() returns a Promise that never gets resolved, so we +# # need to return undefined to avoid a timeout in the spec. +# # TODO: Figure out why `editor.update({autoIndent: true})` never gets resolved. +# return +# +# it "places tab stops correctly", -> +# expect(editor.getSoftTabs()).toBeTruthy() +# editor.setCursorScreenPosition([2, Infinity]) +# editor.insertText ' t3' +# atom.commands.dispatch editorElement, 'snippets:expand' +# expect(editor.getCursorBufferPosition()).toEqual [3, 12] +# atom.commands.dispatch editorElement, 'snippets:next-tab-stop' +# expect(editor.getCursorBufferPosition()).toEqual [4, 4] +# +# it "indents the subsequent lines of the snippet based on the indent level before the snippet is inserted", -> +# editor.setCursorScreenPosition([2, Infinity]) +# editor.insertNewline() +# editor.insertText 't4b' +# atom.commands.dispatch editorElement, 'snippets:expand' +# +# expect(editor.lineTextForBufferRow(3)).toBe " = line 1 {" # 4 + 1 spaces (because the tab stop is invisible) +# expect(editor.lineTextForBufferRow(4)).toBe " line 2" +# expect(editor.lineTextForBufferRow(5)).toBe " }" +# expect(editor.getCursorBufferPosition()).toEqual [3, 4] +# +# it "does not change the relative positioning of the tab stops when inserted multiple times", -> +# editor.setCursorScreenPosition([2, Infinity]) +# editor.insertNewline() +# editor.insertText 't4' +# atom.commands.dispatch editorElement, 'snippets:expand' +# +# expect(editor.getSelectedBufferRange()).toEqual [[3, 9], [3, 10]] +# atom.commands.dispatch editorElement, 'snippets:next-tab-stop' +# expect(editor.getSelectedBufferRange()).toEqual [[4, 6], [4, 13]] +# +# editor.insertText 't4' +# atom.commands.dispatch editorElement, 'snippets:expand' +# +# expect(editor.getSelectedBufferRange()).toEqual [[4, 11], [4, 12]] +# atom.commands.dispatch editorElement, 'snippets:next-tab-stop' +# expect(editor.getSelectedBufferRange()).toEqual [[5, 8], [5, 15]] +# +# editor.setText('') # Clear editor +# editor.insertText 't4' +# atom.commands.dispatch editorElement, 'snippets:expand' +# +# expect(editor.getSelectedBufferRange()).toEqual [[0, 5], [0, 6]] +# atom.commands.dispatch editorElement, 'snippets:next-tab-stop' +# expect(editor.getSelectedBufferRange()).toEqual [[1, 2], [1, 9]] +# +# describe "when multiple snippets match the prefix", -> +# it "expands the snippet that is the longest match for the prefix", -> +# editor.insertText('t113') +# expect(editor.getCursorScreenPosition()).toEqual [0, 4] +# +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "t113 var quicksort = function () {" +# expect(editor.getCursorScreenPosition()).toEqual [0, 6] +# +# editor.undo() +# editor.undo() +# +# editor.insertText("tt1") +# expect(editor.getCursorScreenPosition()).toEqual [0, 3] +# +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "this is another testvar quicksort = function () {" +# expect(editor.getCursorScreenPosition()).toEqual [0, 20] +# +# editor.undo() +# editor.undo() +# +# editor.insertText("@t1") +# expect(editor.getCursorScreenPosition()).toEqual [0, 3] +# +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "@this is a testvar quicksort = function () {" +# expect(editor.getCursorScreenPosition()).toEqual [0, 15] +# +# describe "when the word preceding the cursor ends with a snippet prefix", -> +# it "inserts a tab as normal", -> +# editor.insertText("t1t1t1") +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "t1t1t1 var quicksort = function () {" +# +# describe "when the letters preceding the cursor don't match a snippet", -> +# it "inserts a tab as normal", -> +# editor.insertText("xxte") +# expect(editor.getCursorScreenPosition()).toEqual [0, 4] +# +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "xxte var quicksort = function () {" +# expect(editor.getCursorScreenPosition()).toEqual [0, 6] +# +# describe "when text is selected", -> +# it "inserts a tab as normal", -> +# editor.insertText("t1") +# editor.setSelectedBufferRange([[0, 0], [0, 2]]) +# +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe " t1var quicksort = function () {" +# expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 4]] +# +# describe "when a previous snippet expansion has just been undone", -> +# describe "when the tab stops appear in the middle of the snippet", -> +# 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] +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "first line" +# editor.undo() +# expect(editor.lineTextForBufferRow(0)).toBe "t6" +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "first line" +# +# describe "when the tab stops appear at the beginning and then the end of snippet", -> +# it "expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", -> +# editor.insertText 't6b\n' +# editor.setCursorBufferPosition [0, 3] +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "expanded" +# editor.undo() +# expect(editor.lineTextForBufferRow(0)).toBe "t6b" +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "expanded" +# expect(editor.getCursorBufferPosition()).toEqual([0, 0]) +# +# describe "when the tab stops appear at the end and then the beginning of snippet", -> +# it "expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", -> +# editor.insertText 't6c\n' +# editor.setCursorBufferPosition [0, 3] +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "expanded" +# editor.undo() +# expect(editor.lineTextForBufferRow(0)).toBe "t6c" +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "expanded" +# expect(editor.getCursorBufferPosition()).toEqual([0, 8]) +# +# describe "when the prefix contains non-word characters", -> +# it "selects the non-word characters as part of the prefix", -> +# editor.insertText("@unique") +# expect(editor.getCursorScreenPosition()).toEqual [0, 7] +# +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "@unique seevar quicksort = function () {" +# expect(editor.getCursorScreenPosition()).toEqual [0, 11] +# +# editor.setCursorBufferPosition [10, 0] +# editor.insertText("'@unique") +# +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(10)).toBe "'@unique see" +# expect(editor.getCursorScreenPosition()).toEqual [10, 12] +# +# it "does not select the whitespace before the prefix", -> +# editor.insertText("a; @unique") +# expect(editor.getCursorScreenPosition()).toEqual [0, 10] +# +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "a; @unique seevar quicksort = function () {" +# expect(editor.getCursorScreenPosition()).toEqual [0, 14] +# +# describe "when snippet contains tabstops with or without placeholder", -> +# it "should create two markers", -> +# editor.setCursorScreenPosition([0, 0]) +# editor.insertText('t8') +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" +# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" +# +# expect(editor.getSelectedBufferRange()).toEqual [[0, 17], [0, 21]] +# +# simulateTabKeyEvent() +# expect(editor.getSelectedBufferRange()).toEqual [[1, 20], [1, 20]] +# +# describe "when snippet contains multi-caret tabstops with or without placeholder", -> +# it "should create two markers", -> +# editor.setCursorScreenPosition([0, 0]) +# editor.insertText('t9') +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" +# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" +# editor.insertText('hello') +# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder hello" +# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder hellovar quicksort = function () {" +# +# it "terminates the snippet when cursors are destroyed", -> +# editor.setCursorScreenPosition([0, 0]) +# editor.insertText('t9b') +# simulateTabKeyEvent() +# editor.getCursors()[0].destroy() +# editor.getCursorBufferPosition() +# simulateTabKeyEvent() +# +# expect(editor.lineTextForBufferRow(1)).toEqual("without placeholder ") +# +# it "terminates the snippet expansion if a new cursor moves outside the bounds of the tab stops", -> +# editor.setCursorScreenPosition([0, 0]) +# editor.insertText('t9b') +# simulateTabKeyEvent() +# editor.insertText('test') +# +# editor.getCursors()[0].destroy() +# editor.moveDown() # this should destroy the previous expansion +# editor.moveToBeginningOfLine() +# +# # this should insert whitespace instead of going through tabstops of the previous destroyed snippet +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(2).indexOf(" second")).toBe 0 +# +# it "moves to the second tabstop after a multi-caret tabstop", -> +# editor.setCursorScreenPosition([0, 0]) +# editor.insertText('t9b') +# simulateTabKeyEvent() +# editor.insertText('line 1') +# +# simulateTabKeyEvent() +# editor.insertText('line 2') +# +# simulateTabKeyEvent() +# editor.insertText('line 3') +# +# expect(editor.lineTextForBufferRow(2).indexOf("line 2 ")).toBe -1 +# +# it "mirrors input properly when a tabstop's placeholder refers to another tabstop", -> +# editor.setText('t17') +# editor.setCursorScreenPosition([0, 3]) +# simulateTabKeyEvent() +# editor.insertText("foo") +# expect(editor.getText()).toBe "console.log('uh foo', foo);" +# simulateTabKeyEvent() +# editor.insertText("bar") +# expect(editor.getText()).toBe "console.log('bar', foo);" +# +# describe "when the snippet contains tab stops with transformations", -> +# it "transforms the text typed into the first tab stop before setting it in the transformed tab stop", -> +# editor.setText('t12') +# editor.setCursorScreenPosition([0, 3]) +# simulateTabKeyEvent() +# expect(editor.getText()).toBe("[b][/b]") +# editor.insertText('img src') +# expect(editor.getText()).toBe("[img src][/img]") +# +# it "bundles the transform mutations along with the original manual mutation for the purposes of undo and redo", -> +# editor.setText('t12') +# editor.setCursorScreenPosition([0, 3]) +# simulateTabKeyEvent() +# editor.insertText('i') +# expect(editor.getText()).toBe("[i][/i]") +# +# editor.insertText('mg src') +# expect(editor.getText()).toBe("[img src][/img]") +# +# editor.undo() +# expect(editor.getText()).toBe("[i][/i]") +# +# editor.redo() +# expect(editor.getText()).toBe("[img src][/img]") +# +# it "can pick the right insertion to use as the primary even if a transformed insertion occurs first in the snippet", -> +# editor.setText('t16') +# editor.setCursorScreenPosition([0, 3]) +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe("& Q & q") +# expect(editor.getCursorBufferPosition()).toEqual([0, 7]) +# +# editor.insertText('rst') +# expect(editor.lineTextForBufferRow(0)).toBe("& RST & rst") +# +# it "silently ignores a tab stop without a non-transformed insertion to use as the primary", -> +# editor.setText('t15') +# editor.setCursorScreenPosition([0, 3]) +# simulateTabKeyEvent() +# editor.insertText('a') +# expect(editor.lineTextForBufferRow(0)).toBe(" & a") +# expect(editor.getCursorBufferPosition()).toEqual([0, 4]) +# +# describe "when the snippet contains mirrored tab stops and tab stops with transformations", -> +# it "adds cursors for the mirrors but not the transformations", -> +# editor.setText('t13') +# editor.setCursorScreenPosition([0, 3]) +# simulateTabKeyEvent() +# expect(editor.getCursors().length).toBe(2) +# expect(editor.getText()).toBe """ +# placeholder +# PLACEHOLDER +# +# """ +# +# editor.insertText('foo') +# +# expect(editor.getText()).toBe """ +# foo +# FOO +# foo +# """ +# +# describe "when the snippet contains multiple tab stops, some with transformations and some without", -> +# it "does not get confused", -> +# editor.setText('t14') +# editor.setCursorScreenPosition([0, 3]) +# simulateTabKeyEvent() +# expect(editor.getCursors().length).toBe(2) +# expect(editor.getText()).toBe "placeholder PLACEHOLDER ANOTHER another " +# simulateTabKeyEvent() +# expect(editor.getCursors().length).toBe(2) +# editor.insertText('FOO') +# expect(editor.getText()).toBe """ +# placeholder PLACEHOLDER FOO foo FOO +# """ +# +# describe "when the snippet has a transformed tab stop such that it is possible to move the cursor between the ordinary tab stop and its transformed version without an intermediate step", -> +# it "terminates the snippet upon such a cursor move", -> +# editor.setText('t18') +# editor.setCursorScreenPosition([0, 3]) +# simulateTabKeyEvent() +# expect(editor.getText()).toBe("// \n// ") +# expect(editor.getCursorBufferPosition()).toEqual [0, 3] +# editor.insertText('wat') +# expect(editor.getText()).toBe("// wat\n// ===") +# # Move the cursor down one line, then up one line. This puts the cursor +# # back in its previous position, but the snippet should no longer be +# # active, so when we type more text, it should not be mirrored. +# editor.setCursorScreenPosition([1, 6]) +# editor.setCursorScreenPosition([0, 6]) +# editor.insertText('wat') +# expect(editor.getText()).toBe("// watwat\n// ===") +# +# +# describe "when the snippet contains tab stops with an index >= 10", -> +# it "parses and orders the indices correctly", -> +# editor.setText('t10') +# editor.setCursorScreenPosition([0, 3]) +# simulateTabKeyEvent() +# expect(editor.getText()).toBe "hello large indices" +# expect(editor.getCursorBufferPosition()).toEqual [0, 19] +# simulateTabKeyEvent() +# expect(editor.getCursorBufferPosition()).toEqual [0, 5] +# simulateTabKeyEvent() +# expect(editor.getSelectedBufferRange()).toEqual [[0, 6], [0, 11]] +# +# describe "when there are multiple cursors", -> +# describe "when the cursors share a common snippet prefix", -> +# it "expands the snippet for all cursors and allows simultaneous editing", -> +# editor.insertText('t9') +# editor.setCursorBufferPosition([12, 2]) +# editor.insertText(' t9') +# editor.addCursorAtBufferPosition([0, 2]) +# simulateTabKeyEvent() +# +# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" +# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" +# expect(editor.lineTextForBufferRow(13)).toBe "}; with placeholder test" +# expect(editor.lineTextForBufferRow(14)).toBe "without placeholder " +# +# editor.insertText('hello') +# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder hello" +# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder hellovar quicksort = function () {" +# expect(editor.lineTextForBufferRow(13)).toBe "}; with placeholder hello" +# expect(editor.lineTextForBufferRow(14)).toBe "without placeholder hello" +# +# it "applies transformations identically to single-expansion mode", -> +# editor.setText('t14\nt14') +# editor.setCursorBufferPosition([1, 3]) +# editor.addCursorAtBufferPosition([0, 3]) +# simulateTabKeyEvent() +# +# expect(editor.lineTextForBufferRow(0)).toBe "placeholder PLACEHOLDER ANOTHER another " +# expect(editor.lineTextForBufferRow(1)).toBe "placeholder PLACEHOLDER ANOTHER another " +# +# editor.insertText "testing" +# +# expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing ANOTHER another " +# expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing ANOTHER another " +# +# simulateTabKeyEvent() +# editor.insertText "AGAIN" +# +# expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing AGAIN again AGAIN" +# expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing AGAIN again AGAIN" +# +# it "bundles transform-induced mutations into a single history entry along with their triggering edit, even across multiple snippets", -> +# editor.setText('t14\nt14') +# editor.setCursorBufferPosition([1, 3]) +# editor.addCursorAtBufferPosition([0, 3]) +# simulateTabKeyEvent() +# +# expect(editor.lineTextForBufferRow(0)).toBe "placeholder PLACEHOLDER ANOTHER another " +# expect(editor.lineTextForBufferRow(1)).toBe "placeholder PLACEHOLDER ANOTHER another " +# +# editor.insertText "testing" +# +# expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing ANOTHER another " +# expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing ANOTHER another " +# +# simulateTabKeyEvent() +# editor.insertText "AGAIN" +# +# expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing AGAIN again AGAIN" +# expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing AGAIN again AGAIN" +# +# editor.undo() +# expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing ANOTHER another " +# expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing ANOTHER another " +# +# editor.undo() +# expect(editor.lineTextForBufferRow(0)).toBe "placeholder PLACEHOLDER ANOTHER another " +# expect(editor.lineTextForBufferRow(1)).toBe "placeholder PLACEHOLDER ANOTHER another " +# +# editor.redo() +# expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing ANOTHER another " +# expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing ANOTHER another " +# +# editor.redo() +# expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing AGAIN again AGAIN" +# expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing AGAIN again AGAIN" +# +# describe "when there are many tabstops", -> +# it "moves the cursors between the tab stops for their corresponding snippet when tab and shift-tab are pressed", -> +# editor.addCursorAtBufferPosition([7, 5]) +# editor.addCursorAtBufferPosition([12, 2]) +# editor.insertText('t11') +# simulateTabKeyEvent() +# +# cursors = editor.getCursors() +# expect(cursors.length).toEqual 3 +# +# expect(cursors[0].getBufferPosition()).toEqual [0, 3] +# expect(cursors[1].getBufferPosition()).toEqual [7, 8] +# expect(cursors[2].getBufferPosition()).toEqual [12, 5] +# expect(cursors[0].selection.isEmpty()).toBe true +# expect(cursors[1].selection.isEmpty()).toBe true +# expect(cursors[2].selection.isEmpty()).toBe true +# +# simulateTabKeyEvent() +# expect(cursors[0].getBufferPosition()).toEqual [0, 7] +# expect(cursors[1].getBufferPosition()).toEqual [7, 12] +# expect(cursors[2].getBufferPosition()).toEqual [12, 9] +# expect(cursors[0].selection.isEmpty()).toBe false +# expect(cursors[1].selection.isEmpty()).toBe false +# expect(cursors[2].selection.isEmpty()).toBe false +# expect(cursors[0].selection.getText()).toEqual 'two' +# expect(cursors[1].selection.getText()).toEqual 'two' +# expect(cursors[2].selection.getText()).toEqual 'two' +# +# simulateTabKeyEvent() +# expect(cursors[0].getBufferPosition()).toEqual [0, 13] +# expect(cursors[1].getBufferPosition()).toEqual [7, 18] +# expect(cursors[2].getBufferPosition()).toEqual [12, 15] +# expect(cursors[0].selection.isEmpty()).toBe true +# expect(cursors[1].selection.isEmpty()).toBe true +# expect(cursors[2].selection.isEmpty()).toBe true +# +# simulateTabKeyEvent() +# expect(cursors[0].getBufferPosition()).toEqual [0, 0] +# expect(cursors[1].getBufferPosition()).toEqual [7, 5] +# expect(cursors[2].getBufferPosition()).toEqual [12, 2] +# expect(cursors[0].selection.isEmpty()).toBe true +# expect(cursors[1].selection.isEmpty()).toBe true +# expect(cursors[2].selection.isEmpty()).toBe true +# +# describe "when the cursors do not share common snippet prefixes", -> +# it "inserts tabs as normal", -> +# editor.insertText('t9') +# editor.setCursorBufferPosition([12, 2]) +# editor.insertText(' t8') +# editor.addCursorAtBufferPosition([0, 2]) +# simulateTabKeyEvent() +# expect(editor.lineTextForBufferRow(0)).toBe "t9 var quicksort = function () {" +# expect(editor.lineTextForBufferRow(12)).toBe "}; t8 " +# +# describe "when a snippet is triggered within an existing snippet expansion", -> +# it "ignores the snippet expansion and goes to the next tab stop", -> +# editor.addCursorAtBufferPosition([7, 5]) +# editor.addCursorAtBufferPosition([12, 2]) +# editor.insertText('t11') +# simulateTabKeyEvent() +# simulateTabKeyEvent() +# +# editor.insertText('t1') +# simulateTabKeyEvent() +# +# cursors = editor.getCursors() +# expect(cursors.length).toEqual 3 +# +# expect(cursors[0].getBufferPosition()).toEqual [0, 12] +# expect(cursors[1].getBufferPosition()).toEqual [7, 17] +# expect(cursors[2].getBufferPosition()).toEqual [12, 14] +# expect(cursors[0].selection.isEmpty()).toBe true +# expect(cursors[1].selection.isEmpty()).toBe true +# expect(cursors[2].selection.isEmpty()).toBe true +# expect(editor.lineTextForBufferRow(0)).toBe "one t1 threevar quicksort = function () {" +# expect(editor.lineTextForBufferRow(7)).toBe " }one t1 three" +# expect(editor.lineTextForBufferRow(12)).toBe "};one t1 three" +# +# describe "when the editor is not a pane item (regression)", -> +# it "handles tab stops correctly", -> +# editor = new TextEditor() +# atom.grammars.assignLanguageMode(editor, 'source.js') +# editorElement = editor.getElement() +# +# editor.insertText('t2') +# simulateTabKeyEvent() +# editor.insertText('ABC') +# expect(editor.getText()).toContain('go here first:(ABC)') +# +# editor.undo() +# editor.undo() +# expect(editor.getText()).toBe('t2') +# simulateTabKeyEvent() +# editor.insertText('ABC') +# expect(editor.getText()).toContain('go here first:(ABC)') +# +# describe "when atom://.atom/snippets is opened", -> +# it "opens ~/.atom/snippets.cson", -> +# jasmine.unspy(Snippets, 'getUserSnippetsPath') +# atom.workspace.destroyActivePaneItem() +# configDirPath = temp.mkdirSync('atom-config-dir-') +# spyOn(atom, 'getConfigDirPath').andReturn configDirPath +# atom.workspace.open('atom://.atom/snippets') +# +# waitsFor -> +# atom.workspace.getActiveTextEditor()? +# +# runs -> +# expect(atom.workspace.getActiveTextEditor().getURI()).toBe path.join(configDirPath, 'snippets.cson') +# +# describe "snippet insertion API", -> +# it "will automatically parse snippet definition and replace selection", -> +# editor.setSelectedBufferRange([[0, 4], [0, 13]]) +# Snippets.insert("hello ${1:world}", editor) +# +# expect(editor.lineTextForBufferRow(0)).toBe "var hello world = function () {" +# expect(editor.getSelectedBufferRange()).toEqual [[0, 10], [0, 15]] +# +# describe "when the 'snippets:available' command is triggered", -> +# availableSnippetsView = null +# +# beforeEach -> +# Snippets.add __filename, +# ".source.js": +# "test": +# prefix: "test" +# body: "${1:Test pass you will}, young " +# +# "challenge": +# prefix: "chal" +# body: "$1: ${2:To pass this challenge}" +# +# delete Snippets.availableSnippetsView +# +# atom.commands.dispatch(editorElement, "snippets:available") +# +# waitsFor -> +# atom.workspace.getModalPanels().length is 1 +# +# runs -> +# availableSnippetsView = atom.workspace.getModalPanels()[0].getItem() +# +# it "renders a select list of all available snippets", -> +# expect(availableSnippetsView.selectListView.getSelectedItem().prefix).toBe 'test' +# expect(availableSnippetsView.selectListView.getSelectedItem().name).toBe 'test' +# expect(availableSnippetsView.selectListView.getSelectedItem().bodyText).toBe '${1:Test pass you will}, young ' +# +# availableSnippetsView.selectListView.selectNext() +# +# expect(availableSnippetsView.selectListView.getSelectedItem().prefix).toBe 'chal' +# expect(availableSnippetsView.selectListView.getSelectedItem().name).toBe 'challenge' +# expect(availableSnippetsView.selectListView.getSelectedItem().bodyText).toBe '$1: ${2:To pass this challenge}' +# +# it "writes the selected snippet to the editor as snippet", -> +# availableSnippetsView.selectListView.confirmSelection() +# +# expect(editor.getCursorScreenPosition()).toEqual [0, 18] +# expect(editor.getSelectedText()).toBe 'Test pass you will' +# expect(editor.lineTextForBufferRow(0)).toBe 'Test pass you will, young var quicksort = function () {' +# +# it "closes the dialog when triggered again", -> +# atom.commands.dispatch availableSnippetsView.selectListView.refs.queryEditor.element, 'snippets:available' +# expect(atom.workspace.getModalPanels().length).toBe 0 diff --git a/spec/snippets-spec.js b/spec/snippets-spec.js index 8ce39e1..aed512c 100644 --- a/spec/snippets-spec.js +++ b/spec/snippets-spec.js @@ -1,56 +1,53 @@ -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ const path = require('path'); const temp = require('temp').track(); const Snippets = require('../lib/snippets'); const {TextEditor} = require('atom'); -describe("Snippets extension", function() { - let [editorElement, editor] = Array.from([]); +describe("Snippets extension", () => { + let editorElement, editor; - const simulateTabKeyEvent = function(param) { - if (param == null) { param = {}; } + const simulateTabKeyEvent = (param) => { + if (param == null) { + param = {}; + } const {shift} = param; const event = atom.keymaps.constructor.buildKeydownEvent('tab', {shift, target: editorElement}); - return atom.keymaps.handleKeyboardEvent(event); + atom.keymaps.handleKeyboardEvent(event); }; - beforeEach(function() { + beforeEach(() => { spyOn(Snippets, 'loadAll'); spyOn(Snippets, 'getUserSnippetsPath').andReturn(''); waitsForPromise(() => atom.workspace.open('sample.js')); - waitsForPromise(() => atom.packages.activatePackage('language-javascript')); - waitsForPromise(() => atom.packages.activatePackage('snippets')); - return runs(function() { + runs(() => { editor = atom.workspace.getActiveTextEditor(); - return editorElement = atom.views.getView(editor); + editorElement = atom.views.getView(editor); }); }); - afterEach(() => waitsForPromise(() => atom.packages.deactivatePackage('snippets'))); + afterEach(() => { + waitsForPromise(() => atom.packages.deactivatePackage('snippets')); + }); - describe("provideSnippets interface", function() { + describe("provideSnippets interface", () => { let snippetsInterface = null; - beforeEach(() => snippetsInterface = Snippets.provideSnippets()); + beforeEach(() => { + snippetsInterface = Snippets.provideSnippets(); + }); - describe("bundledSnippetsLoaded", function() { - it("indicates the loaded state of the bundled snippets", function() { + describe("bundledSnippetsLoaded", () => { + it("indicates the loaded state of the bundled snippets", () => { expect(snippetsInterface.bundledSnippetsLoaded()).toBe(false); Snippets.doneLoading(); - return expect(snippetsInterface.bundledSnippetsLoaded()).toBe(true); + expect(snippetsInterface.bundledSnippetsLoaded()).toBe(true); }); - return it("resets the loaded state after snippets is deactivated", function() { + it("resets the loaded state after snippets is deactivated", () => { expect(snippetsInterface.bundledSnippetsLoaded()).toBe(false); Snippets.doneLoading(); expect(snippetsInterface.bundledSnippetsLoaded()).toBe(true); @@ -58,27 +55,29 @@ describe("Snippets extension", function() { waitsForPromise(() => atom.packages.deactivatePackage('snippets')); waitsForPromise(() => atom.packages.activatePackage('snippets')); - return runs(function() { + runs(() => { expect(snippetsInterface.bundledSnippetsLoaded()).toBe(false); Snippets.doneLoading(); - return expect(snippetsInterface.bundledSnippetsLoaded()).toBe(true); + expect(snippetsInterface.bundledSnippetsLoaded()).toBe(true); }); }); }); - return describe("insertSnippet", () => it("can insert a snippet", function() { - editor.setSelectedBufferRange([[0, 4], [0, 13]]); - snippetsInterface.insertSnippet("hello ${1:world}", editor); - return expect(editor.lineTextForBufferRow(0)).toBe("var hello world = function () {"); - })); + describe("insertSnippet", () => { + it("can insert a snippet", () => { + editor.setSelectedBufferRange([[0, 4], [0, 13]]); + snippetsInterface.insertSnippet("hello ${1:world}", editor); + expect(editor.lineTextForBufferRow(0)).toBe("var hello world = function () {"); + }); + }); }); - it("returns false for snippetToExpandUnderCursor if getSnippets returns {}", function() { + it("returns false for snippetToExpandUnderCursor if getSnippets returns {}", () => { const snippets = atom.packages.getActivePackage('snippets').mainModule; - return expect(snippets.snippetToExpandUnderCursor(editor)).toEqual(false); + expect(snippets.snippetToExpandUnderCursor(editor)).toEqual(false); }); - it("ignores invalid snippets in the config", function() { + it("ignores invalid snippets in the config", () => { const snippets = atom.packages.getActivePackage('snippets').mainModule; let invalidSnippets = null; @@ -95,216 +94,216 @@ describe("Snippets extension", function() { expect(snippets.getSnippets(editor)).toEqual({}); invalidSnippets = {a: null}; - return expect(snippets.getSnippets(editor)).toEqual({}); -}); + expect(snippets.getSnippets(editor)).toEqual({}); + }); - describe("when null snippets are present", function() { + describe("when null snippets are present", () => { beforeEach(() => Snippets.add(__filename, { - '.source.js': { + ".source.js": { "some snippet": { prefix: "t1", body: "this is a test" } }, - '.source.js .nope': { + ".source.js .nope": { "some snippet": { prefix: "t1", body: null } } - } - )); + })); - return it("overrides the less-specific defined snippet", function() { + it("overrides the less-specific defined snippet", () => { const snippets = Snippets.provideSnippets(); expect(snippets.snippetsForScopes(['.source.js'])['t1']).toBeTruthy(); - return expect(snippets.snippetsForScopes(['.source.js .nope.not-today'])['t1']).toBeFalsy(); + expect(snippets.snippetsForScopes(['.source.js .nope.not-today'])['t1']).toBeFalsy(); }); }); - describe("when 'tab' is triggered on the editor", function() { - beforeEach(() => Snippets.add(__filename, { - ".source.js": { - "without tab stops": { - prefix: "t1", - body: "this is a test" - }, - - "with only an end tab stop": { - prefix: "t1a", - body: "something $0 strange" - }, - - "overlapping prefix": { - prefix: "tt1", - body: "this is another test" - }, - - "special chars": { - prefix: "@unique", - body: "@unique see" - }, - - "tab stops": { - prefix: "t2", - body: `\ + describe("when 'tab' is triggered on the editor", () => { + beforeEach(() => { + Snippets.add(__filename, { + ".source.js": { + "without tab stops": { + prefix: "t1", + body: "this is a test" + }, + + "with only an end tab stop": { + prefix: "t1a", + body: "something $0 strange" + }, + + "overlapping prefix": { + prefix: "tt1", + body: "this is another test" + }, + + "special chars": { + prefix: "@unique", + body: "@unique see" + }, + + "tab stops": { + prefix: "t2", + body: `\ go here next:($2) and finally go here:($0) go here first:($1) \ ` - }, + }, - "indented second line": { - prefix: "t3", - body: `\ + "indented second line": { + prefix: "t3", + body: `\ line 1 \tline 2$1 $2\ ` - }, + }, - "multiline with indented placeholder tabstop": { - prefix: "t4", - body: `\ + "multiline with indented placeholder tabstop": { + prefix: "t4", + body: `\ line \${1:1} -\${2:body...}\ + \${2:body...}\ ` - }, + }, - "multiline starting with tabstop": { - prefix: "t4b", - body: `\ + "multiline starting with tabstop": { + prefix: "t4b", + body: `\ $1 = line 1 { -line 2 + line 2 }\ ` - }, + }, - "nested tab stops": { - prefix: "t5", - body: '${1:"${2:key}"}: ${3:value}' - }, + "nested tab stops": { + prefix: "t5", + body: '${1:"${2:key}"}: ${3:value}' + }, - "caused problems with undo": { - prefix: "t6", - body: `\ + "caused problems with undo": { + prefix: "t6", + body: `\ first line$1 \${2:placeholder ending second line}\ ` - }, + }, - "tab stops at beginning and then end of snippet": { - prefix: "t6b", - body: "$1expanded$0" - }, + "tab stops at beginning and then end of snippet": { + prefix: "t6b", + body: "$1expanded$0" + }, - "tab stops at end and then beginning of snippet": { - prefix: "t6c", - body: "$0expanded$1" - }, + "tab stops at end and then beginning of snippet": { + prefix: "t6c", + body: "$0expanded$1" + }, - "contains empty lines": { - prefix: "t7", - body: `\ + "contains empty lines": { + prefix: "t7", + body: `\ first line $1 fourth line after blanks $2\ ` - }, - "with/without placeholder": { - prefix: "t8", - body: `\ + }, + "with/without placeholder": { + prefix: "t8", + body: `\ with placeholder \${1:test} without placeholder \${2}\ ` - }, + }, - "multi-caret": { - prefix: "t9", - body: `\ + "multi-caret": { + prefix: "t9", + body: `\ with placeholder \${1:test} without placeholder $1\ ` - }, + }, - "multi-caret-multi-tabstop": { - prefix: "t9b", - body: `\ + "multi-caret-multi-tabstop": { + prefix: "t9b", + body: `\ with placeholder \${1:test} without placeholder $1 second tabstop $2 third tabstop $3\ ` - }, + }, - "large indices": { - prefix: "t10", - body: `\ + "large indices": { + prefix: "t10", + body: `\ hello\${10} \${11:large} indices\${1}\ ` - }, + }, - "no body": { - prefix: "bad1" - }, + "no body": { + prefix: "bad1" + }, - "number body": { - prefix: "bad2", - body: 100 - }, + "number body": { + prefix: "bad2", + body: 100 + }, - "many tabstops": { - prefix: "t11", - body: `\ + "many tabstops": { + prefix: "t11", + body: `\ $0one\${1} \${2:two} three\${3}\ ` - }, + }, - "simple transform": { - prefix: "t12", - body: `\ + "simple transform": { + prefix: "t12", + body: `\ [\${1:b}][/\${1/[ ]+.*$//}]\ ` - }, - "transform with non-transforming mirrors": { - prefix: "t13", - body: `\ + }, + "transform with non-transforming mirrors": { + prefix: "t13", + body: `\ \${1:placeholder}\n\${1/(.)/\\u$1/}\n$1\ ` - }, - "multiple tab stops, some with transforms and some without": { - prefix: "t14", - body: `\ + }, + "multiple tab stops, some with transforms and some without": { + prefix: "t14", + body: `\ \${1:placeholder} \${1/(.)/\\u$1/} $1 \${2:ANOTHER} \${2/^(.*)$/\\L$1/} $2\ ` - }, - "has a transformed tab stop without a corresponding ordinary tab stop": { - prefix: 't15', - body: `\ + }, + "has a transformed tab stop without a corresponding ordinary tab stop": { + prefix: 't15', + body: `\ \${1/(.)/\\u$1/} & $2\ ` - }, - "has a transformed tab stop that occurs before the corresponding ordinary tab stop": { - prefix: 't16', - body: `\ + }, + "has a transformed tab stop that occurs before the corresponding ordinary tab stop": { + prefix: 't16', + body: `\ & \${1/(.)/\\u$1/} & \${1:q}\ ` - }, - "has a placeholder that mirrors another tab stop's content": { - prefix: 't17', - body: "$4console.${3:log}('${2:uh $1}', $1);$0" - }, - "has a transformed tab stop such that it is possible to move the cursor between the ordinary tab stop and its transformed version without an intermediate step": { - prefix: 't18', - body: '// $1\n// ${1/./=/}' + }, + "has a placeholder that mirrors another tab stop's content": { + prefix: 't17', + body: "$4console.${3:log}('${2:uh $1}', $1);$0" + }, + "has a transformed tab stop such that it is possible to move the cursor between the ordinary tab stop and its transformed version without an intermediate step": { + prefix: 't18', + body: '// $1\n// ${1/./=/}' + } } - } - } - )); + }); + }); - it("parses snippets once, reusing cached ones on subsequent queries", function() { + it("parses snippets once, reusing cached ones on subsequent queries", () => { spyOn(Snippets, "getBodyParser").andCallThrough(); editor.insertText("t1"); @@ -333,8 +332,7 @@ $0one\${1} \${2:two} three\${3}\ body: "new snippet" } } - } - ); + }); editor.setText(""); editor.insertText("t1"); @@ -342,43 +340,45 @@ $0one\${1} \${2:two} three\${3}\ expect(Snippets.getBodyParser).toHaveBeenCalled(); expect(editor.lineTextForBufferRow(0)).toBe("new snippet"); - return expect(editor.getCursorScreenPosition()).toEqual([0, 11]); - }); - - describe("when the snippet body is invalid or missing", () => it("does not register the snippet", function() { - editor.setText(''); - editor.insertText('bad1'); - atom.commands.dispatch(editorElement, 'snippets:expand'); - expect(editor.getText()).toBe('bad1'); + expect(editor.getCursorScreenPosition()).toEqual([0, 11]); + }); - editor.setText(''); - editor.setText('bad2'); - atom.commands.dispatch(editorElement, 'snippets:expand'); - return expect(editor.getText()).toBe('bad2'); - })); + describe("when the snippet body is invalid or missing", () => { + it("does not register the snippet", () => { + editor.setText(''); + editor.insertText('bad1'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + expect(editor.getText()).toBe('bad1'); + + editor.setText(''); + editor.setText('bad2'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + expect(editor.getText()).toBe('bad2'); + }); + }); - describe("when the letters preceding the cursor trigger a snippet", function() { - describe("when the snippet contains no tab stops", function() { - it("replaces the prefix with the snippet text and places the cursor at its end", function() { + 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]); simulateTabKeyEvent(); expect(editor.lineTextForBufferRow(0)).toBe("this is a testvar quicksort = function () {"); - return expect(editor.getCursorScreenPosition()).toEqual([0, 14]); - }); + expect(editor.getCursorScreenPosition()).toEqual([0, 14]); + }); - return it("inserts a real tab the next time a tab is pressed after the snippet is expanded", function() { + it("inserts a real tab the next time a tab is pressed after the snippet is expanded", () => { editor.insertText("t1"); simulateTabKeyEvent(); expect(editor.lineTextForBufferRow(0)).toBe("this is a testvar quicksort = function () {"); simulateTabKeyEvent(); - return expect(editor.lineTextForBufferRow(0)).toBe("this is a test var quicksort = function () {"); + expect(editor.lineTextForBufferRow(0)).toBe("this is a test var quicksort = function () {"); }); }); - describe("when the snippet contains tab stops", function() { - it("places the cursor at the first tab-stop, and moves the cursor in response to 'next-tab-stop' events", 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", () => { const markerCountBefore = editor.getMarkerCount(); editor.setCursorScreenPosition([2, 0]); editor.insertText('t2'); @@ -411,161 +411,181 @@ $0one\${1} \${2:two} three\${3}\ simulateTabKeyEvent(); simulateTabKeyEvent(); expect(editor.lineTextForBufferRow(2)).toBe("go here next:(abc) and finally go here:( )"); - return expect(editor.getMarkerCount()).toBe(markerCountBefore); + expect(editor.getMarkerCount()).toBe(markerCountBefore); }); - describe("when tab stops are nested", () => it("destroys the inner tab stop if the outer tab stop is modified", function() { - editor.setText(''); - editor.insertText('t5'); - atom.commands.dispatch(editorElement, 'snippets:expand'); - expect(editor.lineTextForBufferRow(0)).toBe('"key": value'); - expect(editor.getSelectedBufferRange()).toEqual([[0, 0], [0, 5]]); - editor.insertText("foo"); - simulateTabKeyEvent(); - return expect(editor.getSelectedBufferRange()).toEqual([[0, 5], [0, 10]]); - })); - - describe("when the only tab stop is an end stop", () => it("terminates the snippet immediately after moving the cursor to the end stop", function() { - editor.setText(''); - editor.insertText('t1a'); - simulateTabKeyEvent(); - - expect(editor.lineTextForBufferRow(0)).toBe("something strange"); - expect(editor.getCursorBufferPosition()).toEqual([0, 10]); - - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(0)).toBe("something strange"); - return expect(editor.getCursorBufferPosition()).toEqual([0, 12]); - })); - - describe("when tab stops are separated by blank lines", () => it("correctly places the tab stops (regression)", function() { - editor.setText(''); - editor.insertText('t7'); - atom.commands.dispatch(editorElement, 'snippets:expand'); - atom.commands.dispatch(editorElement, 'snippets:next-tab-stop'); - return expect(editor.getCursorBufferPosition()).toEqual([3, 25]); - })); - - describe("when the cursor is moved beyond the bounds of the current tab stop", () => it("terminates the snippet", function() { - editor.setCursorScreenPosition([2, 0]); - editor.insertText('t2'); - simulateTabKeyEvent(); - - editor.moveUp(); - editor.moveLeft(); - simulateTabKeyEvent(); - - expect(editor.lineTextForBufferRow(2)).toBe("go here next:( ) and finally go here:()"); - expect(editor.getCursorBufferPosition()).toEqual([2, 16]); + describe("when tab stops are nested", () => { + it("destroys the inner tab stop if the outer tab stop is modified", () => { + editor.setText(''); + editor.insertText('t5'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + expect(editor.lineTextForBufferRow(0)).toBe('"key": value'); + expect(editor.getSelectedBufferRange()).toEqual([[0, 0], [0, 5]]); + editor.insertText("foo"); + simulateTabKeyEvent(); + expect(editor.getSelectedBufferRange()).toEqual([[0, 5], [0, 10]]); + }); + }); - // test we can terminate with shift-tab - editor.setCursorScreenPosition([4, 0]); - editor.insertText('t2'); - simulateTabKeyEvent(); - simulateTabKeyEvent(); + describe("when the only tab stop is an end stop", () => { + it("terminates the snippet immediately after moving the cursor to the end stop", () => { + editor.setText(''); + editor.insertText('t1a'); + simulateTabKeyEvent(); - editor.moveRight(); - simulateTabKeyEvent({shift: true}); - return expect(editor.getCursorBufferPosition()).toEqual([4, 15]); - })); + expect(editor.lineTextForBufferRow(0)).toBe("something strange"); + expect(editor.getCursorBufferPosition()).toEqual([0, 10]); - describe("when the cursor is moved within the bounds of the current tab stop", () => it("should not terminate the snippet", function() { - editor.setCursorScreenPosition([0, 0]); - editor.insertText('t8'); - simulateTabKeyEvent(); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("something strange"); + expect(editor.getCursorBufferPosition()).toEqual([0, 12]); + }); + }); - expect(editor.lineTextForBufferRow(0)).toBe("with placeholder test"); - editor.moveRight(); - editor.moveLeft(); - editor.insertText("foo"); - expect(editor.lineTextForBufferRow(0)).toBe("with placeholder tesfoot"); + describe("when tab stops are separated by blank lines", () => { + it("correctly places the tab stops (regression)", () => { + editor.setText(''); + editor.insertText('t7'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + atom.commands.dispatch(editorElement, 'snippets:next-tab-stop'); + expect(editor.getCursorBufferPosition()).toEqual([3, 25]); + }); + }); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(1)).toBe("without placeholder var quicksort = function () {"); - editor.insertText("test"); - expect(editor.lineTextForBufferRow(1)).toBe("without placeholder testvar quicksort = function () {"); - editor.moveLeft(); - editor.insertText("foo"); - return expect(editor.lineTextForBufferRow(1)).toBe("without placeholder tesfootvar quicksort = function () {"); - })); - - return describe("when the backspace is press within the bounds of the current tab stop", () => it("should not terminate the snippet", function() { - editor.setCursorScreenPosition([0, 0]); - editor.insertText('t8'); - simulateTabKeyEvent(); + 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'); + simulateTabKeyEvent(); + + editor.moveUp(); + editor.moveLeft(); + simulateTabKeyEvent(); + + expect(editor.lineTextForBufferRow(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'); + simulateTabKeyEvent(); + simulateTabKeyEvent(); + + editor.moveRight(); + simulateTabKeyEvent({shift: true}); + expect(editor.getCursorBufferPosition()).toEqual([4, 15]); + }); + }); - expect(editor.lineTextForBufferRow(0)).toBe("with placeholder test"); - editor.moveRight(); - editor.backspace(); - editor.insertText("foo"); - expect(editor.lineTextForBufferRow(0)).toBe("with placeholder tesfoo"); + describe("when the cursor is moved within the bounds of the current tab stop", () => { + it("should not terminate the snippet", () => { + editor.setCursorScreenPosition([0, 0]); + editor.insertText('t8'); + simulateTabKeyEvent(); + + expect(editor.lineTextForBufferRow(0)).toBe("with placeholder test"); + editor.moveRight(); + editor.moveLeft(); + editor.insertText("foo"); + expect(editor.lineTextForBufferRow(0)).toBe("with placeholder tesfoot"); + + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder var quicksort = function () {"); + editor.insertText("test"); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder testvar quicksort = function () {"); + editor.moveLeft(); + editor.insertText("foo"); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder tesfootvar quicksort = function () {"); + }); + }); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(1)).toBe("without placeholder var quicksort = function () {"); - editor.insertText("test"); - expect(editor.lineTextForBufferRow(1)).toBe("without placeholder testvar quicksort = function () {"); - editor.backspace(); - editor.insertText("foo"); - return expect(editor.lineTextForBufferRow(1)).toBe("without placeholder tesfoovar quicksort = function () {"); - })); + describe("when the backspace is press within the bounds of the current tab stop", () => { + it("should not terminate the snippet", () => { + editor.setCursorScreenPosition([0, 0]); + editor.insertText('t8'); + simulateTabKeyEvent(); + + expect(editor.lineTextForBufferRow(0)).toBe("with placeholder test"); + editor.moveRight(); + editor.backspace(); + editor.insertText("foo"); + expect(editor.lineTextForBufferRow(0)).toBe("with placeholder tesfoo"); + + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder var quicksort = function () {"); + editor.insertText("test"); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder testvar quicksort = function () {"); + editor.backspace(); + editor.insertText("foo"); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder tesfoovar quicksort = function () {"); + }); + }); }); - describe("when the snippet contains hard tabs", function() { - describe("when the edit session is in soft-tabs mode", () => it("translates hard tabs in the snippet to the appropriate number of spaces", function() { - expect(editor.getSoftTabs()).toBeTruthy(); - editor.insertText("t3"); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(1)).toBe(" line 2"); - return expect(editor.getCursorBufferPosition()).toEqual([1, 8]); - })); + 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(editor.getSoftTabs()).toBeTruthy(); + editor.insertText("t3"); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(1)).toBe(" line 2"); + expect(editor.getCursorBufferPosition()).toEqual([1, 8]); + }); + }); - return describe("when the edit session is in hard-tabs mode", () => it("inserts hard tabs in the snippet directly", function() { - editor.setSoftTabs(false); - editor.insertText("t3"); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(1)).toBe("\tline 2"); - return expect(editor.getCursorBufferPosition()).toEqual([1, 7]); - })); - }); + describe("when the edit session is in hard-tabs mode", () => { + it("inserts hard tabs in the snippet directly", () => { + editor.setSoftTabs(false); + editor.insertText("t3"); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(1)).toBe("\tline 2"); + expect(editor.getCursorBufferPosition()).toEqual([1, 7]); + }); + }); + }); - describe("when the snippet prefix is indented", function() { - describe("when the snippet spans a single line", () => it("does not indent the next line", function() { - editor.setCursorScreenPosition([2, Infinity]); - editor.insertText(' t1'); - atom.commands.dispatch(editorElement, 'snippets:expand'); - return expect(editor.lineTextForBufferRow(3)).toBe(" var pivot = items.shift(), current, left = [], right = [];"); - })); + 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'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + expect(editor.lineTextForBufferRow(3)).toBe(" var pivot = items.shift(), current, left = [], right = [];"); + }); + }); - return 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", function() { - expect(editor.getSoftTabs()).toBeTruthy(); - editor.setCursorScreenPosition([2, Infinity]); - editor.insertText(' t3'); - atom.commands.dispatch(editorElement, 'snippets:expand'); - expect(editor.lineTextForBufferRow(2)).toBe(" if (items.length <= 1) return items; line 1"); - expect(editor.lineTextForBufferRow(3)).toBe(" line 2"); - return expect(editor.getCursorBufferPosition()).toEqual([3, 12]); - })); - }); + 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(editor.getSoftTabs()).toBeTruthy(); + editor.setCursorScreenPosition([2, Infinity]); + editor.insertText(' t3'); + atom.commands.dispatch(editorElement, 'snippets:expand'); + expect(editor.lineTextForBufferRow(2)).toBe(" if (items.length <= 1) return items; line 1"); + expect(editor.lineTextForBufferRow(3)).toBe(" line 2"); + expect(editor.getCursorBufferPosition()).toEqual([3, 12]); + }); + }); + }); - describe("when the snippet spans multiple lines", function() { - beforeEach(function() { + describe("when the snippet spans multiple lines", () => { + beforeEach(() => { editor.update({autoIndent: true}); // editor.update() returns a Promise that never gets resolved, so we // need to return undefined to avoid a timeout in the spec. // TODO: Figure out why `editor.update({autoIndent: true})` never gets resolved. }); - it("places tab stops correctly", function() { + it("places tab stops correctly", () => { expect(editor.getSoftTabs()).toBeTruthy(); editor.setCursorScreenPosition([2, Infinity]); editor.insertText(' t3'); atom.commands.dispatch(editorElement, 'snippets:expand'); expect(editor.getCursorBufferPosition()).toEqual([3, 12]); atom.commands.dispatch(editorElement, 'snippets:next-tab-stop'); - return expect(editor.getCursorBufferPosition()).toEqual([4, 4]); - }); + expect(editor.getCursorBufferPosition()).toEqual([4, 4]); + }); - it("indents the subsequent lines of the snippet based on the indent level before the snippet is inserted", function() { + it("indents the subsequent lines of the snippet based on the indent level before the snippet is inserted", () => { editor.setCursorScreenPosition([2, Infinity]); editor.insertNewline(); editor.insertText('t4b'); @@ -574,10 +594,10 @@ $0one\${1} \${2:two} three\${3}\ expect(editor.lineTextForBufferRow(3)).toBe(" = line 1 {"); // 4 + 1 spaces (because the tab stop is invisible) expect(editor.lineTextForBufferRow(4)).toBe(" line 2"); expect(editor.lineTextForBufferRow(5)).toBe(" }"); - return expect(editor.getCursorBufferPosition()).toEqual([3, 4]); - }); + expect(editor.getCursorBufferPosition()).toEqual([3, 4]); + }); - return it("does not change the relative positioning of the tab stops when inserted multiple times", function() { + it("does not change the relative positioning of the tab stops when inserted multiple times", () => { editor.setCursorScreenPosition([2, Infinity]); editor.insertNewline(); editor.insertText('t4'); @@ -600,103 +620,117 @@ $0one\${1} \${2:two} three\${3}\ expect(editor.getSelectedBufferRange()).toEqual([[0, 5], [0, 6]]); atom.commands.dispatch(editorElement, 'snippets:next-tab-stop'); - return expect(editor.getSelectedBufferRange()).toEqual([[1, 2], [1, 9]]); + expect(editor.getSelectedBufferRange()).toEqual([[1, 2], [1, 9]]); + }); }); - }); - return describe("when multiple snippets match the prefix", () => it("expands the snippet that is the longest match for the prefix", function() { - editor.insertText('t113'); - expect(editor.getCursorScreenPosition()).toEqual([0, 4]); + describe("when multiple snippets match the prefix", () => { + it("expands the snippet that is the longest match for the prefix", () => { + editor.insertText('t113'); + expect(editor.getCursorScreenPosition()).toEqual([0, 4]); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(0)).toBe("t113 var quicksort = function () {"); - expect(editor.getCursorScreenPosition()).toEqual([0, 6]); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("t113 var quicksort = function () {"); + expect(editor.getCursorScreenPosition()).toEqual([0, 6]); - editor.undo(); - editor.undo(); + editor.undo(); + editor.undo(); - editor.insertText("tt1"); - expect(editor.getCursorScreenPosition()).toEqual([0, 3]); + editor.insertText("tt1"); + expect(editor.getCursorScreenPosition()).toEqual([0, 3]); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(0)).toBe("this is another testvar quicksort = function () {"); - expect(editor.getCursorScreenPosition()).toEqual([0, 20]); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("this is another testvar quicksort = function () {"); + expect(editor.getCursorScreenPosition()).toEqual([0, 20]); - editor.undo(); - editor.undo(); + editor.undo(); + editor.undo(); - editor.insertText("@t1"); - expect(editor.getCursorScreenPosition()).toEqual([0, 3]); + editor.insertText("@t1"); + expect(editor.getCursorScreenPosition()).toEqual([0, 3]); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(0)).toBe("@this is a testvar quicksort = function () {"); - return expect(editor.getCursorScreenPosition()).toEqual([0, 15]); - })); - }); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("@this is a testvar quicksort = function () {"); + expect(editor.getCursorScreenPosition()).toEqual([0, 15]); + }); + }); + }); - describe("when the word preceding the cursor ends with a snippet prefix", () => it("inserts a tab as normal", function() { - editor.insertText("t1t1t1"); - simulateTabKeyEvent(); - return expect(editor.lineTextForBufferRow(0)).toBe("t1t1t1 var quicksort = function () {"); - })); + describe("when the word preceding the cursor ends with a snippet prefix", () => { + it("inserts a tab as normal", () => { + editor.insertText("t1t1t1"); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("t1t1t1 var quicksort = function () {"); + }); + }); - describe("when the letters preceding the cursor don't match a snippet", () => it("inserts a tab as normal", function() { - editor.insertText("xxte"); - expect(editor.getCursorScreenPosition()).toEqual([0, 4]); + describe("when the letters preceding the cursor don't match a snippet", () => { + it("inserts a tab as normal", () => { + editor.insertText("xxte"); + expect(editor.getCursorScreenPosition()).toEqual([0, 4]); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(0)).toBe("xxte var quicksort = function () {"); - return expect(editor.getCursorScreenPosition()).toEqual([0, 6]); - })); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("xxte var quicksort = function () {"); + expect(editor.getCursorScreenPosition()).toEqual([0, 6]); + }); + }); - describe("when text is selected", () => it("inserts a tab as normal", function() { - editor.insertText("t1"); - editor.setSelectedBufferRange([[0, 0], [0, 2]]); + describe("when text is selected", () => { + it("inserts a tab as normal", () => { + editor.insertText("t1"); + editor.setSelectedBufferRange([[0, 0], [0, 2]]); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(0)).toBe(" t1var quicksort = function () {"); - return expect(editor.getSelectedBufferRange()).toEqual([[0, 0], [0, 4]]); - })); - - describe("when a previous snippet expansion has just been undone", function() { - describe("when the tab stops appear in the middle of the snippet", () => it("expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", function() { - editor.insertText('t6\n'); - editor.setCursorBufferPosition([0, 2]); simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(0)).toBe("first line"); - editor.undo(); - expect(editor.lineTextForBufferRow(0)).toBe("t6"); - simulateTabKeyEvent(); - return expect(editor.lineTextForBufferRow(0)).toBe("first line"); - })); + expect(editor.lineTextForBufferRow(0)).toBe(" t1var quicksort = function () {"); + expect(editor.getSelectedBufferRange()).toEqual([[0, 0], [0, 4]]); + }); + }); - describe("when the tab stops appear at the beginning and then the end of snippet", () => it("expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", function() { - editor.insertText('t6b\n'); - editor.setCursorBufferPosition([0, 3]); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(0)).toBe("expanded"); - editor.undo(); - expect(editor.lineTextForBufferRow(0)).toBe("t6b"); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(0)).toBe("expanded"); - return expect(editor.getCursorBufferPosition()).toEqual([0, 0]); - })); + describe("when a previous snippet expansion has just been undone", () => { + describe("when the tab stops appear in the middle of the snippet", () => { + 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]); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("first line"); + editor.undo(); + expect(editor.lineTextForBufferRow(0)).toBe("t6"); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("first line"); + }); + }); - return describe("when the tab stops appear at the end and then the beginning of snippet", () => it("expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", function() { - editor.insertText('t6c\n'); - editor.setCursorBufferPosition([0, 3]); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(0)).toBe("expanded"); - editor.undo(); - expect(editor.lineTextForBufferRow(0)).toBe("t6c"); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(0)).toBe("expanded"); - return expect(editor.getCursorBufferPosition()).toEqual([0, 8]); - })); + describe("when the tab stops appear at the beginning and then the end of snippet", () => { + it("expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", () => { + editor.insertText('t6b\n'); + editor.setCursorBufferPosition([0, 3]); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("expanded"); + editor.undo(); + expect(editor.lineTextForBufferRow(0)).toBe("t6b"); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("expanded"); + expect(editor.getCursorBufferPosition()).toEqual([0, 0]); + }); + }); + + describe("when the tab stops appear at the end and then the beginning of snippet", () => { + it("expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", () => { + editor.insertText('t6c\n'); + editor.setCursorBufferPosition([0, 3]); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("expanded"); + editor.undo(); + expect(editor.lineTextForBufferRow(0)).toBe("t6c"); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("expanded"); + expect(editor.getCursorBufferPosition()).toEqual([0, 8]); + }); + }); }); - describe("when the prefix contains non-word characters", function() { - it("selects the non-word characters as part of the prefix", function() { + describe("when the prefix contains non-word characters", () => { + it("selects the non-word characters as part of the prefix", () => { editor.insertText("@unique"); expect(editor.getCursorScreenPosition()).toEqual([0, 7]); @@ -709,34 +743,36 @@ $0one\${1} \${2:two} three\${3}\ simulateTabKeyEvent(); expect(editor.lineTextForBufferRow(10)).toBe("'@unique see"); - return expect(editor.getCursorScreenPosition()).toEqual([10, 12]); - }); + expect(editor.getCursorScreenPosition()).toEqual([10, 12]); + }); - return it("does not select the whitespace before the prefix", function() { + it("does not select the whitespace before the prefix", () => { editor.insertText("a; @unique"); expect(editor.getCursorScreenPosition()).toEqual([0, 10]); simulateTabKeyEvent(); expect(editor.lineTextForBufferRow(0)).toBe("a; @unique seevar quicksort = function () {"); - return expect(editor.getCursorScreenPosition()).toEqual([0, 14]); + expect(editor.getCursorScreenPosition()).toEqual([0, 14]); + }); }); - }); - describe("when snippet contains tabstops with or without placeholder", () => it("should create two markers", function() { - editor.setCursorScreenPosition([0, 0]); - editor.insertText('t8'); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(0)).toBe("with placeholder test"); - expect(editor.lineTextForBufferRow(1)).toBe("without placeholder var quicksort = function () {"); + describe("when snippet contains tabstops with or without placeholder", () => { + it("should create two markers", () => { + editor.setCursorScreenPosition([0, 0]); + editor.insertText('t8'); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("with placeholder test"); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder var quicksort = function () {"); - expect(editor.getSelectedBufferRange()).toEqual([[0, 17], [0, 21]]); + expect(editor.getSelectedBufferRange()).toEqual([[0, 17], [0, 21]]); - simulateTabKeyEvent(); - return expect(editor.getSelectedBufferRange()).toEqual([[1, 20], [1, 20]]); - })); + simulateTabKeyEvent(); + expect(editor.getSelectedBufferRange()).toEqual([[1, 20], [1, 20]]); + }); + }); - describe("when snippet contains multi-caret tabstops with or without placeholder", function() { - it("should create two markers", function() { + describe("when snippet contains multi-caret tabstops with or without placeholder", () => { + it("should create two markers", () => { editor.setCursorScreenPosition([0, 0]); editor.insertText('t9'); simulateTabKeyEvent(); @@ -744,10 +780,10 @@ $0one\${1} \${2:two} three\${3}\ expect(editor.lineTextForBufferRow(1)).toBe("without placeholder var quicksort = function () {"); editor.insertText('hello'); expect(editor.lineTextForBufferRow(0)).toBe("with placeholder hello"); - return expect(editor.lineTextForBufferRow(1)).toBe("without placeholder hellovar quicksort = function () {"); + expect(editor.lineTextForBufferRow(1)).toBe("without placeholder hellovar quicksort = function () {"); }); - it("terminates the snippet when cursors are destroyed", function() { + it("terminates the snippet when cursors are destroyed", () => { editor.setCursorScreenPosition([0, 0]); editor.insertText('t9b'); simulateTabKeyEvent(); @@ -755,10 +791,10 @@ $0one\${1} \${2:two} three\${3}\ editor.getCursorBufferPosition(); simulateTabKeyEvent(); - return expect(editor.lineTextForBufferRow(1)).toEqual("without placeholder "); + expect(editor.lineTextForBufferRow(1)).toEqual("without placeholder "); }); - it("terminates the snippet expansion if a new cursor moves outside the bounds of the tab stops", function() { + it("terminates the snippet expansion if a new cursor moves outside the bounds of the tab stops", () => { editor.setCursorScreenPosition([0, 0]); editor.insertText('t9b'); simulateTabKeyEvent(); @@ -770,10 +806,10 @@ $0one\${1} \${2:two} three\${3}\ // this should insert whitespace instead of going through tabstops of the previous destroyed snippet simulateTabKeyEvent(); - return expect(editor.lineTextForBufferRow(2).indexOf(" second")).toBe(0); + expect(editor.lineTextForBufferRow(2).indexOf(" second")).toBe(0); }); - it("moves to the second tabstop after a multi-caret tabstop", function() { + it("moves to the second tabstop after a multi-caret tabstop", () => { editor.setCursorScreenPosition([0, 0]); editor.insertText('t9b'); simulateTabKeyEvent(); @@ -785,10 +821,10 @@ $0one\${1} \${2:two} three\${3}\ simulateTabKeyEvent(); editor.insertText('line 3'); - return expect(editor.lineTextForBufferRow(2).indexOf("line 2 ")).toBe(-1); + expect(editor.lineTextForBufferRow(2).indexOf("line 2 ")).toBe(-1); }); - return it("mirrors input properly when a tabstop's placeholder refers to another tabstop", function() { + it("mirrors input properly when a tabstop's placeholder refers to another tabstop", () => { editor.setText('t17'); editor.setCursorScreenPosition([0, 3]); simulateTabKeyEvent(); @@ -796,21 +832,21 @@ $0one\${1} \${2:two} three\${3}\ expect(editor.getText()).toBe("console.log('uh foo', foo);"); simulateTabKeyEvent(); editor.insertText("bar"); - return expect(editor.getText()).toBe("console.log('bar', foo);"); + expect(editor.getText()).toBe("console.log('bar', foo);"); }); }); - describe("when the snippet contains tab stops with transformations", function() { - it("transforms the text typed into the first tab stop before setting it in the transformed tab stop", function() { + describe("when the snippet contains tab stops with transformations", () => { + it("transforms the text typed into the first tab stop before setting it in the transformed tab stop", () => { editor.setText('t12'); editor.setCursorScreenPosition([0, 3]); simulateTabKeyEvent(); expect(editor.getText()).toBe("[b][/b]"); editor.insertText('img src'); - return expect(editor.getText()).toBe("[img src][/img]"); + expect(editor.getText()).toBe("[img src][/img]"); }); - it("bundles the transform mutations along with the original manual mutation for the purposes of undo and redo", function() { + it("bundles the transform mutations along with the original manual mutation for the purposes of undo and redo", () => { editor.setText('t12'); editor.setCursorScreenPosition([0, 3]); simulateTabKeyEvent(); @@ -824,10 +860,10 @@ $0one\${1} \${2:two} three\${3}\ expect(editor.getText()).toBe("[i][/i]"); editor.redo(); - return expect(editor.getText()).toBe("[img src][/img]"); + expect(editor.getText()).toBe("[img src][/img]"); }); - it("can pick the right insertion to use as the primary even if a transformed insertion occurs first in the snippet", function() { + it("can pick the right insertion to use as the primary even if a transformed insertion occurs first in the snippet", () => { editor.setText('t16'); editor.setCursorScreenPosition([0, 3]); simulateTabKeyEvent(); @@ -835,89 +871,93 @@ $0one\${1} \${2:two} three\${3}\ expect(editor.getCursorBufferPosition()).toEqual([0, 7]); editor.insertText('rst'); - return expect(editor.lineTextForBufferRow(0)).toBe("& RST & rst"); + expect(editor.lineTextForBufferRow(0)).toBe("& RST & rst"); }); - return it("silently ignores a tab stop without a non-transformed insertion to use as the primary", function() { + it("silently ignores a tab stop without a non-transformed insertion to use as the primary", () => { editor.setText('t15'); editor.setCursorScreenPosition([0, 3]); simulateTabKeyEvent(); editor.insertText('a'); expect(editor.lineTextForBufferRow(0)).toBe(" & a"); - return expect(editor.getCursorBufferPosition()).toEqual([0, 4]); + expect(editor.getCursorBufferPosition()).toEqual([0, 4]); }); }); - describe("when the snippet contains mirrored tab stops and tab stops with transformations", () => it("adds cursors for the mirrors but not the transformations", function() { - editor.setText('t13'); - editor.setCursorScreenPosition([0, 3]); - simulateTabKeyEvent(); - expect(editor.getCursors().length).toBe(2); - expect(editor.getText()).toBe(`\ + describe("when the snippet contains mirrored tab stops and tab stops with transformations", () => { + it("adds cursors for the mirrors but not the transformations", () => { + editor.setText('t13'); + editor.setCursorScreenPosition([0, 3]); + simulateTabKeyEvent(); + expect(editor.getCursors().length).toBe(2); + expect(editor.getText()).toBe(`\ placeholder PLACEHOLDER \ ` - ); + ); - editor.insertText('foo'); + editor.insertText('foo'); - return expect(editor.getText()).toBe(`\ + expect(editor.getText()).toBe(`\ foo FOO foo\ ` - ); - })); - - describe("when the snippet contains multiple tab stops, some with transformations and some without", () => it("does not get confused", function() { - editor.setText('t14'); - editor.setCursorScreenPosition([0, 3]); - simulateTabKeyEvent(); - expect(editor.getCursors().length).toBe(2); - expect(editor.getText()).toBe("placeholder PLACEHOLDER ANOTHER another "); - simulateTabKeyEvent(); - expect(editor.getCursors().length).toBe(2); - editor.insertText('FOO'); - return expect(editor.getText()).toBe(`\ -placeholder PLACEHOLDER FOO foo FOO\ -` - ); - })); + ); + }); + }); - describe("when the snippet has a transformed tab stop such that it is possible to move the cursor between the ordinary tab stop and its transformed version without an intermediate step", () => it("terminates the snippet upon such a cursor move", function() { - editor.setText('t18'); - editor.setCursorScreenPosition([0, 3]); - simulateTabKeyEvent(); - expect(editor.getText()).toBe("// \n// "); - expect(editor.getCursorBufferPosition()).toEqual([0, 3]); - editor.insertText('wat'); - expect(editor.getText()).toBe("// wat\n// ==="); - // Move the cursor down one line, then up one line. This puts the cursor - // back in its previous position, but the snippet should no longer be - // active, so when we type more text, it should not be mirrored. - editor.setCursorScreenPosition([1, 6]); - editor.setCursorScreenPosition([0, 6]); - editor.insertText('wat'); - return expect(editor.getText()).toBe("// watwat\n// ==="); - })); + describe("when the snippet contains multiple tab stops, some with transformations and some without", () => { + it("does not get confused", () => { + editor.setText('t14'); + editor.setCursorScreenPosition([0, 3]); + simulateTabKeyEvent(); + expect(editor.getCursors().length).toBe(2); + expect(editor.getText()).toBe("placeholder PLACEHOLDER ANOTHER another "); + simulateTabKeyEvent(); + expect(editor.getCursors().length).toBe(2); + editor.insertText('FOO'); + expect(editor.getText()).toBe("placeholder PLACEHOLDER FOO foo FOO"); + }); + }); + describe("when the snippet has a transformed tab stop such that it is possible to move the cursor between the ordinary tab stop and its transformed version without an intermediate step", () => { + it("terminates the snippet upon such a cursor move", () => { + editor.setText('t18'); + editor.setCursorScreenPosition([0, 3]); + simulateTabKeyEvent(); + expect(editor.getText()).toBe("// \n// "); + expect(editor.getCursorBufferPosition()).toEqual([0, 3]); + editor.insertText('wat'); + expect(editor.getText()).toBe("// wat\n// ==="); + // Move the cursor down one line, then up one line. This puts the cursor + // back in its previous position, but the snippet should no longer be + // active, so when we type more text, it should not be mirrored. + editor.setCursorScreenPosition([1, 6]); + editor.setCursorScreenPosition([0, 6]); + editor.insertText('wat'); + expect(editor.getText()).toBe("// watwat\n// ==="); + }); + }); - describe("when the snippet contains tab stops with an index >= 10", () => it("parses and orders the indices correctly", function() { - editor.setText('t10'); - editor.setCursorScreenPosition([0, 3]); - simulateTabKeyEvent(); - expect(editor.getText()).toBe("hello large indices"); - expect(editor.getCursorBufferPosition()).toEqual([0, 19]); - simulateTabKeyEvent(); - expect(editor.getCursorBufferPosition()).toEqual([0, 5]); - simulateTabKeyEvent(); - return expect(editor.getSelectedBufferRange()).toEqual([[0, 6], [0, 11]]); - })); + describe("when the snippet contains tab stops with an index >= 10", () => { + it("parses and orders the indices correctly", () => { + editor.setText('t10'); + editor.setCursorScreenPosition([0, 3]); + simulateTabKeyEvent(); + expect(editor.getText()).toBe("hello large indices"); + expect(editor.getCursorBufferPosition()).toEqual([0, 19]); + simulateTabKeyEvent(); + expect(editor.getCursorBufferPosition()).toEqual([0, 5]); + simulateTabKeyEvent(); + expect(editor.getSelectedBufferRange()).toEqual([[0, 6], [0, 11]]); + }); + }); - describe("when there are multiple cursors", function() { - describe("when the cursors share a common snippet prefix", function() { - it("expands the snippet for all cursors and allows simultaneous editing", function() { + describe("when there are multiple cursors", () => { + describe("when the cursors share a common snippet prefix", () => { + it("expands the snippet for all cursors and allows simultaneous editing", () => { editor.insertText('t9'); editor.setCursorBufferPosition([12, 2]); editor.insertText(' t9'); @@ -933,10 +973,10 @@ placeholder PLACEHOLDER FOO foo FOO\ expect(editor.lineTextForBufferRow(0)).toBe("with placeholder hello"); expect(editor.lineTextForBufferRow(1)).toBe("without placeholder hellovar quicksort = function () {"); expect(editor.lineTextForBufferRow(13)).toBe("}; with placeholder hello"); - return expect(editor.lineTextForBufferRow(14)).toBe("without placeholder hello"); + expect(editor.lineTextForBufferRow(14)).toBe("without placeholder hello"); }); - it("applies transformations identically to single-expansion mode", function() { + it("applies transformations identically to single-expansion mode", () => { editor.setText('t14\nt14'); editor.setCursorBufferPosition([1, 3]); editor.addCursorAtBufferPosition([0, 3]); @@ -954,10 +994,10 @@ placeholder PLACEHOLDER FOO foo FOO\ editor.insertText("AGAIN"); expect(editor.lineTextForBufferRow(0)).toBe("testing TESTING testing AGAIN again AGAIN"); - return expect(editor.lineTextForBufferRow(1)).toBe("testing TESTING testing AGAIN again AGAIN"); + expect(editor.lineTextForBufferRow(1)).toBe("testing TESTING testing AGAIN again AGAIN"); }); - it("bundles transform-induced mutations into a single history entry along with their triggering edit, even across multiple snippets", function() { + it("bundles transform-induced mutations into a single history entry along with their triggering edit, even across multiple snippets", () => { editor.setText('t14\nt14'); editor.setCursorBufferPosition([1, 3]); editor.addCursorAtBufferPosition([0, 3]); @@ -991,132 +1031,146 @@ placeholder PLACEHOLDER FOO foo FOO\ editor.redo(); expect(editor.lineTextForBufferRow(0)).toBe("testing TESTING testing AGAIN again AGAIN"); - return expect(editor.lineTextForBufferRow(1)).toBe("testing TESTING testing AGAIN again AGAIN"); + expect(editor.lineTextForBufferRow(1)).toBe("testing TESTING testing AGAIN again AGAIN"); + }); + + describe("when there are many tabstops", () => { + it("moves the cursors between the tab stops for their corresponding snippet when tab and shift-tab are pressed", () => { + editor.addCursorAtBufferPosition([7, 5]); + editor.addCursorAtBufferPosition([12, 2]); + editor.insertText('t11'); + simulateTabKeyEvent(); + + const cursors = editor.getCursors(); + expect(cursors.length).toEqual(3); + + expect(cursors[0].getBufferPosition()).toEqual([0, 3]); + expect(cursors[1].getBufferPosition()).toEqual([7, 8]); + expect(cursors[2].getBufferPosition()).toEqual([12, 5]); + expect(cursors[0].selection.isEmpty()).toBe(true); + expect(cursors[1].selection.isEmpty()).toBe(true); + expect(cursors[2].selection.isEmpty()).toBe(true); + + simulateTabKeyEvent(); + expect(cursors[0].getBufferPosition()).toEqual([0, 7]); + expect(cursors[1].getBufferPosition()).toEqual([7, 12]); + expect(cursors[2].getBufferPosition()).toEqual([12, 9]); + expect(cursors[0].selection.isEmpty()).toBe(false); + expect(cursors[1].selection.isEmpty()).toBe(false); + expect(cursors[2].selection.isEmpty()).toBe(false); + expect(cursors[0].selection.getText()).toEqual('two'); + expect(cursors[1].selection.getText()).toEqual('two'); + expect(cursors[2].selection.getText()).toEqual('two'); + + simulateTabKeyEvent(); + expect(cursors[0].getBufferPosition()).toEqual([0, 13]); + expect(cursors[1].getBufferPosition()).toEqual([7, 18]); + expect(cursors[2].getBufferPosition()).toEqual([12, 15]); + expect(cursors[0].selection.isEmpty()).toBe(true); + expect(cursors[1].selection.isEmpty()).toBe(true); + expect(cursors[2].selection.isEmpty()).toBe(true); + + simulateTabKeyEvent(); + expect(cursors[0].getBufferPosition()).toEqual([0, 0]); + expect(cursors[1].getBufferPosition()).toEqual([7, 5]); + expect(cursors[2].getBufferPosition()).toEqual([12, 2]); + expect(cursors[0].selection.isEmpty()).toBe(true); + expect(cursors[1].selection.isEmpty()).toBe(true); + expect(cursors[2].selection.isEmpty()).toBe(true); + }); + }); + }); + + describe("when the cursors do not share common snippet prefixes", () => { + it("inserts tabs as normal", () => { + editor.insertText('t9'); + editor.setCursorBufferPosition([12, 2]); + editor.insertText(' t8'); + editor.addCursorAtBufferPosition([0, 2]); + simulateTabKeyEvent(); + expect(editor.lineTextForBufferRow(0)).toBe("t9 var quicksort = function () {"); + expect(editor.lineTextForBufferRow(12)).toBe("}; t8 "); }); + }); - return describe("when there are many tabstops", () => it("moves the cursors between the tab stops for their corresponding snippet when tab and shift-tab are pressed", function() { + describe("when a snippet is triggered within an existing snippet expansion", () => { + it("ignores the snippet expansion and goes to the next tab stop", () => { editor.addCursorAtBufferPosition([7, 5]); editor.addCursorAtBufferPosition([12, 2]); editor.insertText('t11'); simulateTabKeyEvent(); + simulateTabKeyEvent(); + + editor.insertText('t1'); + simulateTabKeyEvent(); const cursors = editor.getCursors(); expect(cursors.length).toEqual(3); - expect(cursors[0].getBufferPosition()).toEqual([0, 3]); - expect(cursors[1].getBufferPosition()).toEqual([7, 8]); - expect(cursors[2].getBufferPosition()).toEqual([12, 5]); + expect(cursors[0].getBufferPosition()).toEqual([0, 12]); + expect(cursors[1].getBufferPosition()).toEqual([7, 17]); + expect(cursors[2].getBufferPosition()).toEqual([12, 14]); expect(cursors[0].selection.isEmpty()).toBe(true); expect(cursors[1].selection.isEmpty()).toBe(true); expect(cursors[2].selection.isEmpty()).toBe(true); - - simulateTabKeyEvent(); - expect(cursors[0].getBufferPosition()).toEqual([0, 7]); - expect(cursors[1].getBufferPosition()).toEqual([7, 12]); - expect(cursors[2].getBufferPosition()).toEqual([12, 9]); - expect(cursors[0].selection.isEmpty()).toBe(false); - expect(cursors[1].selection.isEmpty()).toBe(false); - expect(cursors[2].selection.isEmpty()).toBe(false); - expect(cursors[0].selection.getText()).toEqual('two'); - expect(cursors[1].selection.getText()).toEqual('two'); - expect(cursors[2].selection.getText()).toEqual('two'); - - simulateTabKeyEvent(); - expect(cursors[0].getBufferPosition()).toEqual([0, 13]); - expect(cursors[1].getBufferPosition()).toEqual([7, 18]); - expect(cursors[2].getBufferPosition()).toEqual([12, 15]); - expect(cursors[0].selection.isEmpty()).toBe(true); - expect(cursors[1].selection.isEmpty()).toBe(true); - expect(cursors[2].selection.isEmpty()).toBe(true); - - simulateTabKeyEvent(); - expect(cursors[0].getBufferPosition()).toEqual([0, 0]); - expect(cursors[1].getBufferPosition()).toEqual([7, 5]); - expect(cursors[2].getBufferPosition()).toEqual([12, 2]); - expect(cursors[0].selection.isEmpty()).toBe(true); - expect(cursors[1].selection.isEmpty()).toBe(true); - return expect(cursors[2].selection.isEmpty()).toBe(true); - })); + expect(editor.lineTextForBufferRow(0)).toBe("one t1 threevar quicksort = function () {"); + expect(editor.lineTextForBufferRow(7)).toBe(" }one t1 three"); + expect(editor.lineTextForBufferRow(12)).toBe("};one t1 three"); + }); }); + }); - describe("when the cursors do not share common snippet prefixes", () => it("inserts tabs as normal", function() { - editor.insertText('t9'); - editor.setCursorBufferPosition([12, 2]); - editor.insertText(' t8'); - editor.addCursorAtBufferPosition([0, 2]); - simulateTabKeyEvent(); - expect(editor.lineTextForBufferRow(0)).toBe("t9 var quicksort = function () {"); - return expect(editor.lineTextForBufferRow(12)).toBe("}; t8 "); - })); - - return describe("when a snippet is triggered within an existing snippet expansion", () => it("ignores the snippet expansion and goes to the next tab stop", function() { - editor.addCursorAtBufferPosition([7, 5]); - editor.addCursorAtBufferPosition([12, 2]); - editor.insertText('t11'); - simulateTabKeyEvent(); - simulateTabKeyEvent(); + describe("when the editor is not a pane item (regression)", () => { + it("handles tab stops correctly", () => { + editor = new TextEditor(); + atom.grammars.assignLanguageMode(editor, 'source.js'); + editorElement = editor.getElement(); - editor.insertText('t1'); + editor.insertText('t2'); simulateTabKeyEvent(); + editor.insertText('ABC'); + expect(editor.getText()).toContain('go here first:(ABC)'); - const cursors = editor.getCursors(); - expect(cursors.length).toEqual(3); - - expect(cursors[0].getBufferPosition()).toEqual([0, 12]); - expect(cursors[1].getBufferPosition()).toEqual([7, 17]); - expect(cursors[2].getBufferPosition()).toEqual([12, 14]); - expect(cursors[0].selection.isEmpty()).toBe(true); - expect(cursors[1].selection.isEmpty()).toBe(true); - expect(cursors[2].selection.isEmpty()).toBe(true); - expect(editor.lineTextForBufferRow(0)).toBe("one t1 threevar quicksort = function () {"); - expect(editor.lineTextForBufferRow(7)).toBe(" }one t1 three"); - return expect(editor.lineTextForBufferRow(12)).toBe("};one t1 three"); - })); + editor.undo(); + editor.undo(); + expect(editor.getText()).toBe('t2'); + simulateTabKeyEvent(); + editor.insertText('ABC'); + expect(editor.getText()).toContain('go here first:(ABC)'); + }); }); - - return describe("when the editor is not a pane item (regression)", () => it("handles tab stops correctly", function() { - editor = new TextEditor(); - atom.grammars.assignLanguageMode(editor, 'source.js'); - editorElement = editor.getElement(); - - editor.insertText('t2'); - simulateTabKeyEvent(); - editor.insertText('ABC'); - expect(editor.getText()).toContain('go here first:(ABC)'); - - editor.undo(); - editor.undo(); - expect(editor.getText()).toBe('t2'); - simulateTabKeyEvent(); - editor.insertText('ABC'); - return expect(editor.getText()).toContain('go here first:(ABC)'); - })); }); - describe("when atom://.atom/snippets is opened", () => it("opens ~/.atom/snippets.cson", function() { - jasmine.unspy(Snippets, 'getUserSnippetsPath'); - atom.workspace.destroyActivePaneItem(); - const configDirPath = temp.mkdirSync('atom-config-dir-'); - spyOn(atom, 'getConfigDirPath').andReturn(configDirPath); - atom.workspace.open('atom://.atom/snippets'); + describe("when atom://.atom/snippets is opened", () => { + it("opens ~/.atom/snippets.cson", () => { + jasmine.unspy(Snippets, 'getUserSnippetsPath'); + atom.workspace.destroyActivePaneItem(); + const configDirPath = temp.mkdirSync('atom-config-dir-'); + spyOn(atom, 'getConfigDirPath').andReturn(configDirPath); + atom.workspace.open('atom://.atom/snippets'); - waitsFor(() => atom.workspace.getActiveTextEditor() != null); + waitsFor(() => atom.workspace.getActiveTextEditor() != null); - return runs(() => expect(atom.workspace.getActiveTextEditor().getURI()).toBe(path.join(configDirPath, 'snippets.cson'))); - })); + runs(() => { + expect(atom.workspace.getActiveTextEditor().getURI()).toBe(path.join(configDirPath, 'snippets.cson')); + }); + }); + }); - describe("snippet insertion API", () => it("will automatically parse snippet definition and replace selection", function() { - editor.setSelectedBufferRange([[0, 4], [0, 13]]); - Snippets.insert("hello ${1:world}", editor); + describe("snippet insertion API", () => { + it("will automatically parse snippet definition and replace selection", () => { + editor.setSelectedBufferRange([[0, 4], [0, 13]]); + Snippets.insert("hello ${1:world}", editor); - expect(editor.lineTextForBufferRow(0)).toBe("var hello world = function () {"); - return expect(editor.getSelectedBufferRange()).toEqual([[0, 10], [0, 15]]); -})); + expect(editor.lineTextForBufferRow(0)).toBe("var hello world = function () {"); + expect(editor.getSelectedBufferRange()).toEqual([[0, 10], [0, 15]]); + }); + }); - return describe("when the 'snippets:available' command is triggered", function() { + describe("when the 'snippets:available' command is triggered", () => { let availableSnippetsView = null; - beforeEach(function() { + beforeEach(() => { Snippets.add(__filename, { ".source.js": { "test": { @@ -1138,10 +1192,12 @@ placeholder PLACEHOLDER FOO foo FOO\ waitsFor(() => atom.workspace.getModalPanels().length === 1); - return runs(() => availableSnippetsView = atom.workspace.getModalPanels()[0].getItem()); + runs(() => { + availableSnippetsView = atom.workspace.getModalPanels()[0].getItem(); + }); }); - it("renders a select list of all available snippets", function() { + it("renders a select list of all available snippets", () => { expect(availableSnippetsView.selectListView.getSelectedItem().prefix).toBe('test'); expect(availableSnippetsView.selectListView.getSelectedItem().name).toBe('test'); expect(availableSnippetsView.selectListView.getSelectedItem().bodyText).toBe('${1:Test pass you will}, young '); @@ -1150,20 +1206,20 @@ placeholder PLACEHOLDER FOO foo FOO\ expect(availableSnippetsView.selectListView.getSelectedItem().prefix).toBe('chal'); expect(availableSnippetsView.selectListView.getSelectedItem().name).toBe('challenge'); - return expect(availableSnippetsView.selectListView.getSelectedItem().bodyText).toBe('$1: ${2:To pass this challenge}'); + expect(availableSnippetsView.selectListView.getSelectedItem().bodyText).toBe('$1: ${2:To pass this challenge}'); }); - it("writes the selected snippet to the editor as snippet", function() { + it("writes the selected snippet to the editor as snippet", () => { availableSnippetsView.selectListView.confirmSelection(); expect(editor.getCursorScreenPosition()).toEqual([0, 18]); expect(editor.getSelectedText()).toBe('Test pass you will'); - return expect(editor.lineTextForBufferRow(0)).toBe('Test pass you will, young var quicksort = function () {'); + expect(editor.lineTextForBufferRow(0)).toBe('Test pass you will, young var quicksort = function () {'); }); - return it("closes the dialog when triggered again", function() { + it("closes the dialog when triggered again", () => { atom.commands.dispatch(availableSnippetsView.selectListView.refs.queryEditor.element, 'snippets:available'); - return expect(atom.workspace.getModalPanels().length).toBe(0); + expect(atom.workspace.getModalPanels().length).toBe(0); }); }); }); From 0ae6c40f97b345a395da6d33122716a0c98c72c2 Mon Sep 17 00:00:00 2001 From: Benjamin Gray Date: Sun, 29 Dec 2019 23:29:17 +1000 Subject: [PATCH 3/6] remove old CS files --- spec/body-parser-spec.coffee | 243 ------- spec/snippet-loading-spec.coffee | 268 -------- spec/snippets-spec.coffee | 1072 ------------------------------ 3 files changed, 1583 deletions(-) delete mode 100644 spec/body-parser-spec.coffee delete mode 100644 spec/snippet-loading-spec.coffee delete mode 100644 spec/snippets-spec.coffee diff --git a/spec/body-parser-spec.coffee b/spec/body-parser-spec.coffee deleted file mode 100644 index 4840f0a..0000000 --- a/spec/body-parser-spec.coffee +++ /dev/null @@ -1,243 +0,0 @@ -# BodyParser = require '../lib/snippet-body-parser' -# -# describe "Snippet Body Parser", -> -# it "breaks a snippet body into lines, with each line containing tab stops at the appropriate position", -> -# bodyTree = BodyParser.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 = BodyParser.parse """ -# module ${1:ActiveRecord::${TM_FILENAME/(?:\\A|_)([A-Za-z0-9]+)(?:\\.rb)?/(?2::\\u$1)/g}} -# """ -# -# expect(bodyTree).toEqual [ -# "module ", -# { -# "index": 1, -# "content": ["ActiveRecord::", ""] -# } -# ] -# -# it "skips escaped tabstops", -> -# bodyTree = BodyParser.parse """ -# snippet $1 escaped \\$2 \\\\$3 -# """ -# -# expect(bodyTree).toEqual [ -# "snippet ", -# { -# index: 1, -# content: [] -# }, -# " escaped $2 \\", -# { -# index: 3, -# content: [] -# } -# ] -# -# it "includes escaped right-braces", -> -# bodyTree = BodyParser.parse """ -# snippet ${1:{\\}} -# """ -# -# expect(bodyTree).toEqual [ -# "snippet ", -# { -# index: 1, -# content: ["{}"] -# } -# ] -# -# it "parses a snippet with transformations", -> -# bodyTree = BodyParser.parse """ -# <${1:p}>$0 -# """ -# expect(bodyTree).toEqual [ -# '<', -# {index: 1, content: ['p']}, -# '>', -# {index: 0, content: []}, -# '' -# ] -# -# it "parses a snippet with multiple tab stops with transformations", -> -# bodyTree = BodyParser.parse """ -# ${1:placeholder} ${1/(.)/\\u$1/} $1 ${2:ANOTHER} ${2/^(.*)$/\\L$1/} $2 -# """ -# -# expect(bodyTree).toEqual [ -# {index: 1, content: ['placeholder']}, -# ' ', -# { -# index: 1, -# content: [], -# substitution: { -# find: /(.)/g, -# replace: [ -# {escape: 'u'}, -# {backreference: 1} -# ] -# } -# }, -# ' ', -# {index: 1, content: []}, -# ' ', -# {index: 2, content: ['ANOTHER']}, -# ' ', -# { -# index: 2, -# content: [], -# substitution: { -# find: /^(.*)$/g, -# replace: [ -# {escape: 'L'}, -# {backreference: 1} -# ] -# } -# }, -# ' ', -# {index: 2, content: []}, -# ] -# -# -# it "parses a snippet with transformations and mirrors", -> -# bodyTree = BodyParser.parse """ -# ${1:placeholder}\n${1/(.)/\\u$1/}\n$1 -# """ -# -# expect(bodyTree).toEqual [ -# {index: 1, content: ['placeholder']}, -# '\n', -# { -# index: 1, -# content: [], -# substitution: { -# find: /(.)/g, -# replace: [ -# {escape: 'u'}, -# {backreference: 1} -# ] -# } -# }, -# '\n', -# {index: 1, content: []} -# ] -# -# it "parses a snippet with a format string and case-control flags", -> -# bodyTree = BodyParser.parse """ -# <${1:p}>$0 -# """ -# -# expect(bodyTree).toEqual [ -# '<', -# {index: 1, content: ['p']}, -# '>', -# {index: 0, content: []}, -# '' -# ] -# -# it "parses a snippet with an escaped forward slash in a transform", -> -# # Annoyingly, a forward slash needs to be double-backslashed just like the -# # other escapes. -# bodyTree = BodyParser.parse """ -# <${1:p}>$0 -# """ -# -# expect(bodyTree).toEqual [ -# '<', -# {index: 1, content: ['p']}, -# '>', -# {index: 0, content: []}, -# '' -# ] -# -# it "parses a snippet with a placeholder that mirrors another tab stop's content", -> -# bodyTree = BodyParser.parse """ -# $4console.${3:log}('${2:$1}', $1);$0 -# """ -# -# expect(bodyTree).toEqual [ -# {index: 4, content: []}, -# 'console.', -# {index: 3, content: ['log']}, -# '(\'', -# { -# index: 2, content: [ -# {index: 1, content: []} -# ] -# }, -# '\', ', -# {index: 1, content: []}, -# ');', -# {index: 0, content: []} -# ] -# -# it "parses a snippet with a placeholder that mixes text and tab stop references", -> -# bodyTree = BodyParser.parse """ -# $4console.${3:log}('${2:uh $1}', $1);$0 -# """ -# -# expect(bodyTree).toEqual [ -# {index: 4, content: []}, -# 'console.', -# {index: 3, content: ['log']}, -# '(\'', -# { -# index: 2, content: [ -# 'uh ', -# {index: 1, content: []} -# ] -# }, -# '\', ', -# {index: 1, content: []}, -# ');', -# {index: 0, content: []} -# ] diff --git a/spec/snippet-loading-spec.coffee b/spec/snippet-loading-spec.coffee deleted file mode 100644 index 273186f..0000000 --- a/spec/snippet-loading-spec.coffee +++ /dev/null @@ -1,268 +0,0 @@ -# path = require 'path' -# fs = require 'fs-plus' -# temp = require('temp').track() -# -# describe "Snippet Loading", -> -# [configDirPath, snippetsService] = [] -# -# beforeEach -> -# configDirPath = temp.mkdirSync('atom-config-dir-') -# spyOn(atom, 'getConfigDirPath').andReturn configDirPath -# -# spyOn(console, 'warn') -# spyOn(atom.notifications, 'addError') if atom.notifications? -# -# spyOn(atom.packages, 'getLoadedPackages').andReturn [ -# atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-snippets')) -# atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-broken-snippets')), -# ] -# -# afterEach -> -# waitsForPromise -> -# Promise.resolve(atom.packages.deactivatePackages('snippets')) -# runs -> -# jasmine.unspy(atom.packages, 'getLoadedPackages') -# -# activateSnippetsPackage = -> -# waitsForPromise -> -# atom.packages.activatePackage("snippets").then ({mainModule}) -> -# snippetsService = mainModule.provideSnippets() -# mainModule.loaded = false -# -# waitsFor "all snippets to load", 3000, -> -# snippetsService.bundledSnippetsLoaded() -# -# it "loads the bundled snippet template snippets", -> -# activateSnippetsPackage() -# -# runs -> -# jsonSnippet = snippetsService.snippetsForScopes(['.source.json'])['snip'] -# expect(jsonSnippet.name).toBe 'Atom Snippet' -# expect(jsonSnippet.prefix).toBe 'snip' -# expect(jsonSnippet.body).toContain '"prefix":' -# expect(jsonSnippet.body).toContain '"body":' -# expect(jsonSnippet.tabStopList.length).toBeGreaterThan(0) -# -# csonSnippet = snippetsService.snippetsForScopes(['.source.coffee'])['snip'] -# expect(csonSnippet.name).toBe 'Atom Snippet' -# expect(csonSnippet.prefix).toBe 'snip' -# expect(csonSnippet.body).toContain "'prefix':" -# expect(csonSnippet.body).toContain "'body':" -# expect(csonSnippet.tabStopList.length).toBeGreaterThan(0) -# -# it "loads non-hidden snippet files from atom packages with snippets directories", -> -# activateSnippetsPackage() -# -# runs -> -# snippet = snippetsService.snippetsForScopes(['.test'])['test'] -# expect(snippet.prefix).toBe 'test' -# expect(snippet.body).toBe 'testing 123' -# -# snippet = snippetsService.snippetsForScopes(['.test'])['testd'] -# expect(snippet.prefix).toBe 'testd' -# expect(snippet.body).toBe 'testing 456' -# expect(snippet.description).toBe 'a description' -# expect(snippet.descriptionMoreURL).toBe 'http://google.com' -# -# snippet = snippetsService.snippetsForScopes(['.test'])['testlabelleft'] -# expect(snippet.prefix).toBe 'testlabelleft' -# expect(snippet.body).toBe 'testing 456' -# expect(snippet.leftLabel).toBe 'a label' -# -# snippet = snippetsService.snippetsForScopes(['.test'])['testhtmllabels'] -# expect(snippet.prefix).toBe 'testhtmllabels' -# expect(snippet.body).toBe 'testing 456' -# expect(snippet.leftLabelHTML).toBe 'Label' -# expect(snippet.rightLabelHTML).toBe 'Label' -# -# it "logs a warning if package snippets files cannot be parsed", -> -# activateSnippetsPackage() -# -# runs -> -# # Warn about invalid-file, but don't even try to parse a hidden file -# expect(console.warn.calls.length).toBeGreaterThan 0 -# expect(console.warn.mostRecentCall.args[0]).toMatch(/Error reading.*package-with-broken-snippets/) -# -# describe "::loadPackageSnippets(callback)", -> -# beforeEach -> -# # simulate a list of packages where the javascript core package is returned at the end -# atom.packages.getLoadedPackages.andReturn [ -# atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-snippets')) -# atom.packages.loadPackage('language-javascript') -# ] -# -# it "allows other packages to override core packages' snippets", -> -# waitsForPromise -> -# atom.packages.activatePackage("language-javascript") -# -# activateSnippetsPackage() -# -# runs -> -# snippet = snippetsService.snippetsForScopes(['.source.js'])['log'] -# expect(snippet.body).toBe "from-a-community-package" -# -# describe "::onDidLoadSnippets(callback)", -> -# it "invokes listeners when all snippets are loaded", -> -# loadedCallback = null -# -# waitsFor "package to activate", (done) -> -# atom.packages.activatePackage("snippets").then ({mainModule}) -> -# mainModule.onDidLoadSnippets(loadedCallback = jasmine.createSpy('onDidLoadSnippets callback')) -# done() -# -# waitsFor "onDidLoad callback to be called", -> loadedCallback.callCount > 0 -# -# describe "when ~/.atom/snippets.json exists", -> -# beforeEach -> -# fs.writeFileSync path.join(configDirPath, 'snippets.json'), """ -# { -# ".foo": { -# "foo snippet": { -# "prefix": "foo", -# "body": "bar1" -# } -# } -# } -# """ -# activateSnippetsPackage() -# -# it "loads the snippets from that file", -> -# snippet = null -# -# waitsFor -> -# snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] -# -# runs -> -# expect(snippet.name).toBe 'foo snippet' -# expect(snippet.prefix).toBe "foo" -# expect(snippet.body).toBe "bar1" -# -# describe "when that file changes", -> -# it "reloads the snippets", -> -# fs.writeFileSync path.join(configDirPath, 'snippets.json'), """ -# { -# ".foo": { -# "foo snippet": { -# "prefix": "foo", -# "body": "bar2" -# } -# } -# } -# """ -# -# waitsFor "snippets to be changed", -> -# snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] -# snippet?.body is 'bar2' -# -# runs -> -# fs.writeFileSync path.join(configDirPath, 'snippets.json'), "" -# -# waitsFor "snippets to be removed", -> -# not snippetsService.snippetsForScopes(['.foo'])['foo'] -# -# describe "when ~/.atom/snippets.cson exists", -> -# beforeEach -> -# fs.writeFileSync path.join(configDirPath, 'snippets.cson'), """ -# ".foo": -# "foo snippet": -# "prefix": "foo" -# "body": "bar1" -# """ -# activateSnippetsPackage() -# -# it "loads the snippets from that file", -> -# snippet = null -# -# waitsFor -> -# snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] -# -# runs -> -# expect(snippet.name).toBe 'foo snippet' -# expect(snippet.prefix).toBe "foo" -# expect(snippet.body).toBe "bar1" -# -# describe "when that file changes", -> -# it "reloads the snippets", -> -# fs.writeFileSync path.join(configDirPath, 'snippets.cson'), """ -# ".foo": -# "foo snippet": -# "prefix": "foo" -# "body": "bar2" -# """ -# -# waitsFor "snippets to be changed", -> -# snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] -# snippet?.body is 'bar2' -# -# runs -> -# fs.writeFileSync path.join(configDirPath, 'snippets.cson'), "" -# -# waitsFor "snippets to be removed", -> -# snippet = snippetsService.snippetsForScopes(['.foo'])['foo'] -# not snippet? -# -# it "notifies the user when the user snippets file cannot be loaded", -> -# fs.writeFileSync path.join(configDirPath, 'snippets.cson'), """ -# ".junk"::: -# """ -# -# activateSnippetsPackage() -# -# runs -> -# expect(console.warn).toHaveBeenCalled() -# expect(atom.notifications.addError).toHaveBeenCalled() if atom.notifications? -# -# describe "packages-with-snippets-disabled feature", -> -# it "disables no snippets if the config option is empty", -> -# originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') -# atom.config.set('core.packagesWithSnippetsDisabled', []) -# -# activateSnippetsPackage() -# runs -> -# snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) -# expect(Object.keys(snippets).length).toBe 1 -# atom.config.set('core.packagesWithSnippetsDisabled', originalConfig) -# -# it "still includes a disabled package's snippets in the list of unparsed snippets", -> -# originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') -# atom.config.set('core.packagesWithSnippetsDisabled', []) -# -# activateSnippetsPackage() -# runs -> -# atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets']) -# allSnippets = snippetsService.getUnparsedSnippets() -# scopedSnippet = allSnippets.find (s) -> -# s.selectorString is '.package-with-snippets-unique-scope' -# expect(scopedSnippet).not.toBe undefined -# originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') -# -# it "never loads a package's snippets when that package is disabled in config", -> -# originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') -# atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets']) -# -# activateSnippetsPackage() -# runs -> -# snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) -# expect(Object.keys(snippets).length).toBe 0 -# atom.config.set('core.packagesWithSnippetsDisabled', originalConfig) -# -# it "unloads and/or reloads snippets from a package if the config option is changed after activation", -> -# originalConfig = atom.config.get('core.packagesWithSnippetsDisabled') -# atom.config.set('core.packagesWithSnippetsDisabled', []) -# -# activateSnippetsPackage() -# runs -> -# snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) -# expect(Object.keys(snippets).length).toBe 1 -# -# # Disable it. -# atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets']) -# snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) -# expect(Object.keys(snippets).length).toBe 0 -# -# # Re-enable it. -# atom.config.set('core.packagesWithSnippetsDisabled', []) -# snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']) -# expect(Object.keys(snippets).length).toBe 1 -# -# atom.config.set('core.packagesWithSnippetsDisabled', originalConfig) diff --git a/spec/snippets-spec.coffee b/spec/snippets-spec.coffee deleted file mode 100644 index 6e9b88b..0000000 --- a/spec/snippets-spec.coffee +++ /dev/null @@ -1,1072 +0,0 @@ -# path = require 'path' -# temp = require('temp').track() -# Snippets = require '../lib/snippets' -# {TextEditor} = require 'atom' -# -# describe "Snippets extension", -> -# [editorElement, editor] = [] -# -# simulateTabKeyEvent = ({shift}={}) -> -# event = atom.keymaps.constructor.buildKeydownEvent('tab', {shift, target: editorElement}) -# atom.keymaps.handleKeyboardEvent(event) -# -# beforeEach -> -# spyOn(Snippets, 'loadAll') -# spyOn(Snippets, 'getUserSnippetsPath').andReturn('') -# -# waitsForPromise -> -# atom.workspace.open('sample.js') -# -# waitsForPromise -> -# atom.packages.activatePackage('language-javascript') -# -# waitsForPromise -> -# atom.packages.activatePackage('snippets') -# -# runs -> -# editor = atom.workspace.getActiveTextEditor() -# editorElement = atom.views.getView(editor) -# -# afterEach -> -# waitsForPromise -> -# atom.packages.deactivatePackage('snippets') -# -# describe "provideSnippets interface", -> -# snippetsInterface = null -# -# beforeEach -> -# snippetsInterface = Snippets.provideSnippets() -# -# describe "bundledSnippetsLoaded", -> -# it "indicates the loaded state of the bundled snippets", -> -# expect(snippetsInterface.bundledSnippetsLoaded()).toBe false -# Snippets.doneLoading() -# expect(snippetsInterface.bundledSnippetsLoaded()).toBe true -# -# it "resets the loaded state after snippets is deactivated", -> -# expect(snippetsInterface.bundledSnippetsLoaded()).toBe false -# Snippets.doneLoading() -# expect(snippetsInterface.bundledSnippetsLoaded()).toBe true -# -# waitsForPromise -> atom.packages.deactivatePackage('snippets') -# waitsForPromise -> atom.packages.activatePackage('snippets') -# -# runs -> -# expect(snippetsInterface.bundledSnippetsLoaded()).toBe false -# Snippets.doneLoading() -# expect(snippetsInterface.bundledSnippetsLoaded()).toBe true -# -# describe "insertSnippet", -> -# it "can insert a snippet", -> -# editor.setSelectedBufferRange([[0, 4], [0, 13]]) -# snippetsInterface.insertSnippet("hello ${1:world}", editor) -# expect(editor.lineTextForBufferRow(0)).toBe "var hello world = function () {" -# -# it "returns false for snippetToExpandUnderCursor if getSnippets returns {}", -> -# snippets = atom.packages.getActivePackage('snippets').mainModule -# expect(snippets.snippetToExpandUnderCursor(editor)).toEqual false -# -# it "ignores invalid snippets in the config", -> -# snippets = atom.packages.getActivePackage('snippets').mainModule -# -# invalidSnippets = null -# spyOn(snippets.scopedPropertyStore, 'getPropertyValue').andCallFake -> invalidSnippets -# expect(snippets.getSnippets(editor)).toEqual {} -# -# invalidSnippets = 'test' -# expect(snippets.getSnippets(editor)).toEqual {} -# -# invalidSnippets = [] -# expect(snippets.getSnippets(editor)).toEqual {} -# -# invalidSnippets = 3 -# expect(snippets.getSnippets(editor)).toEqual {} -# -# invalidSnippets = {a: null} -# expect(snippets.getSnippets(editor)).toEqual {} -# -# describe "when null snippets are present", -> -# beforeEach -> -# Snippets.add __filename, -# '.source.js': -# "some snippet": -# prefix: "t1" -# body: "this is a test" -# -# '.source.js .nope': -# "some snippet": -# prefix: "t1" -# body: null -# -# it "overrides the less-specific defined snippet", -> -# snippets = Snippets.provideSnippets() -# expect(snippets.snippetsForScopes(['.source.js'])['t1']).toBeTruthy() -# expect(snippets.snippetsForScopes(['.source.js .nope.not-today'])['t1']).toBeFalsy() -# -# describe "when 'tab' is triggered on the editor", -> -# beforeEach -> -# Snippets.add __filename, -# ".source.js": -# "without tab stops": -# prefix: "t1" -# body: "this is a test" -# -# "with only an end tab stop": -# prefix: "t1a" -# body: "something $0 strange" -# -# "overlapping prefix": -# prefix: "tt1" -# body: "this is another test" -# -# "special chars": -# prefix: "@unique" -# body: "@unique see" -# -# "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 -# $2 -# """ -# -# "multiline with indented placeholder tabstop": -# prefix: "t4" -# body: """ -# line ${1:1} -# ${2:body...} -# """ -# -# "multiline starting with tabstop": -# prefix: "t4b" -# body: """ -# $1 = line 1 { -# line 2 -# } -# """ -# -# "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} -# """ -# -# "tab stops at beginning and then end of snippet": -# prefix: "t6b" -# body: "$1expanded$0" -# -# "tab stops at end and then beginning of snippet": -# prefix: "t6c" -# body: "$0expanded$1" -# -# "contains empty lines": -# prefix: "t7" -# body: """ -# first line $1 -# -# -# fourth line after blanks $2 -# """ -# "with/without placeholder": -# prefix: "t8" -# body: """ -# with placeholder ${1:test} -# without placeholder ${2} -# """ -# -# "multi-caret": -# prefix: "t9" -# body: """ -# with placeholder ${1:test} -# without placeholder $1 -# """ -# -# "multi-caret-multi-tabstop": -# prefix: "t9b" -# body: """ -# with placeholder ${1:test} -# without placeholder $1 -# second tabstop $2 -# third tabstop $3 -# """ -# -# "large indices": -# prefix: "t10" -# body: """ -# hello${10} ${11:large} indices${1} -# """ -# -# "no body": -# prefix: "bad1" -# -# "number body": -# prefix: "bad2" -# body: 100 -# -# "many tabstops": -# prefix: "t11" -# body: """ -# $0one${1} ${2:two} three${3} -# """ -# -# "simple transform": -# prefix: "t12" -# body: """ -# [${1:b}][/${1/[ ]+.*$//}] -# """ -# "transform with non-transforming mirrors": -# prefix: "t13" -# body: """ -# ${1:placeholder}\n${1/(.)/\\u$1/}\n$1 -# """ -# "multiple tab stops, some with transforms and some without": -# prefix: "t14" -# body: """ -# ${1:placeholder} ${1/(.)/\\u$1/} $1 ${2:ANOTHER} ${2/^(.*)$/\\L$1/} $2 -# """ -# "has a transformed tab stop without a corresponding ordinary tab stop": -# prefix: 't15' -# body: """ -# ${1/(.)/\\u$1/} & $2 -# """ -# "has a transformed tab stop that occurs before the corresponding ordinary tab stop": -# prefix: 't16' -# body: """ -# & ${1/(.)/\\u$1/} & ${1:q} -# """ -# "has a placeholder that mirrors another tab stop's content": -# prefix: 't17' -# body: "$4console.${3:log}('${2:uh $1}', $1);$0" -# "has a transformed tab stop such that it is possible to move the cursor between the ordinary tab stop and its transformed version without an intermediate step": -# prefix: 't18' -# body: '// $1\n// ${1/./=/}' -# -# it "parses snippets once, reusing cached ones on subsequent queries", -> -# spyOn(Snippets, "getBodyParser").andCallThrough() -# -# editor.insertText("t1") -# simulateTabKeyEvent() -# -# expect(Snippets.getBodyParser).toHaveBeenCalled() -# expect(editor.lineTextForBufferRow(0)).toBe "this is a testvar quicksort = function () {" -# expect(editor.getCursorScreenPosition()).toEqual [0, 14] -# -# Snippets.getBodyParser.reset() -# -# editor.setText("") -# editor.insertText("t1") -# simulateTabKeyEvent() -# -# expect(Snippets.getBodyParser).not.toHaveBeenCalled() -# expect(editor.lineTextForBufferRow(0)).toBe "this is a test" -# expect(editor.getCursorScreenPosition()).toEqual [0, 14] -# -# Snippets.getBodyParser.reset() -# -# Snippets.add __filename, -# ".source.js": -# "invalidate previous snippet": -# prefix: "t1" -# body: "new snippet" -# -# editor.setText("") -# editor.insertText("t1") -# simulateTabKeyEvent() -# -# expect(Snippets.getBodyParser).toHaveBeenCalled() -# expect(editor.lineTextForBufferRow(0)).toBe "new snippet" -# expect(editor.getCursorScreenPosition()).toEqual [0, 11] -# -# describe "when the snippet body is invalid or missing", -> -# it "does not register the snippet", -> -# editor.setText('') -# editor.insertText('bad1') -# atom.commands.dispatch editorElement, 'snippets:expand' -# expect(editor.getText()).toBe 'bad1' -# -# editor.setText('') -# editor.setText('bad2') -# atom.commands.dispatch editorElement, 'snippets:expand' -# expect(editor.getText()).toBe 'bad2' -# -# 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] -# -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(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") -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "this is a testvar quicksort = function () {" -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(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.getMarkerCount() -# editor.setCursorScreenPosition([2, 0]) -# editor.insertText('t2') -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(2)).toBe "go here next:() and finally go here:()" -# expect(editor.lineTextForBufferRow(3)).toBe "go here first:()" -# expect(editor.lineTextForBufferRow(4)).toBe " if (items.length <= 1) return items;" -# expect(editor.getSelectedBufferRange()).toEqual [[3, 15], [3, 15]] -# -# simulateTabKeyEvent() -# expect(editor.getSelectedBufferRange()).toEqual [[2, 14], [2, 14]] -# editor.insertText 'abc' -# -# simulateTabKeyEvent() -# expect(editor.getSelectedBufferRange()).toEqual [[2, 40], [2, 40]] -# -# # tab backwards -# simulateTabKeyEvent(shift: true) -# expect(editor.getSelectedBufferRange()).toEqual [[2, 14], [2, 17]] # should highlight text typed at tab stop -# -# simulateTabKeyEvent(shift: true) -# expect(editor.getSelectedBufferRange()).toEqual [[3, 15], [3, 15]] -# -# # shift-tab on first tab-stop does nothing -# simulateTabKeyEvent(shift: true) -# expect(editor.getCursorScreenPosition()).toEqual [3, 15] -# -# # tab through all tab stops, then tab on last stop to terminate snippet -# simulateTabKeyEvent() -# simulateTabKeyEvent() -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(2)).toBe "go here next:(abc) and finally go here:( )" -# expect(editor.getMarkerCount()).toBe markerCountBefore -# -# describe "when tab stops are nested", -> -# it "destroys the inner tab stop if the outer tab stop is modified", -> -# editor.setText('') -# editor.insertText 't5' -# atom.commands.dispatch editorElement, 'snippets:expand' -# expect(editor.lineTextForBufferRow(0)).toBe '"key": value' -# expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 5]] -# editor.insertText("foo") -# simulateTabKeyEvent() -# expect(editor.getSelectedBufferRange()).toEqual [[0, 5], [0, 10]] -# -# describe "when the only tab stop is an end stop", -> -# it "terminates the snippet immediately after moving the cursor to the end stop", -> -# editor.setText('') -# editor.insertText 't1a' -# simulateTabKeyEvent() -# -# expect(editor.lineTextForBufferRow(0)).toBe "something strange" -# expect(editor.getCursorBufferPosition()).toEqual [0, 10] -# -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "something strange" -# expect(editor.getCursorBufferPosition()).toEqual [0, 12] -# -# describe "when tab stops are separated by blank lines", -> -# it "correctly places the tab stops (regression)", -> -# editor.setText('') -# editor.insertText 't7' -# atom.commands.dispatch editorElement, 'snippets:expand' -# atom.commands.dispatch editorElement, 'snippets:next-tab-stop' -# expect(editor.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') -# simulateTabKeyEvent() -# -# editor.moveUp() -# editor.moveLeft() -# simulateTabKeyEvent() -# -# expect(editor.lineTextForBufferRow(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') -# simulateTabKeyEvent() -# simulateTabKeyEvent() -# -# editor.moveRight() -# simulateTabKeyEvent(shift: true) -# expect(editor.getCursorBufferPosition()).toEqual [4, 15] -# -# describe "when the cursor is moved within the bounds of the current tab stop", -> -# it "should not terminate the snippet", -> -# editor.setCursorScreenPosition([0, 0]) -# editor.insertText('t8') -# simulateTabKeyEvent() -# -# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" -# editor.moveRight() -# editor.moveLeft() -# editor.insertText("foo") -# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder tesfoot" -# -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" -# editor.insertText("test") -# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder testvar quicksort = function () {" -# editor.moveLeft() -# editor.insertText("foo") -# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder tesfootvar quicksort = function () {" -# -# describe "when the backspace is press within the bounds of the current tab stop", -> -# it "should not terminate the snippet", -> -# editor.setCursorScreenPosition([0, 0]) -# editor.insertText('t8') -# simulateTabKeyEvent() -# -# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" -# editor.moveRight() -# editor.backspace() -# editor.insertText("foo") -# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder tesfoo" -# -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" -# editor.insertText("test") -# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder testvar quicksort = function () {" -# editor.backspace() -# editor.insertText("foo") -# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder tesfoovar quicksort = function () {" -# -# 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(editor.getSoftTabs()).toBeTruthy() -# editor.insertText("t3") -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(1)).toBe " line 2" -# expect(editor.getCursorBufferPosition()).toEqual [1, 8] -# -# describe "when the edit session is in hard-tabs mode", -> -# it "inserts hard tabs in the snippet directly", -> -# editor.setSoftTabs(false) -# editor.insertText("t3") -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(1)).toBe "\tline 2" -# expect(editor.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' -# atom.commands.dispatch editorElement, 'snippets:expand' -# expect(editor.lineTextForBufferRow(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(editor.getSoftTabs()).toBeTruthy() -# editor.setCursorScreenPosition([2, Infinity]) -# editor.insertText ' t3' -# atom.commands.dispatch editorElement, 'snippets:expand' -# expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items; line 1" -# expect(editor.lineTextForBufferRow(3)).toBe " line 2" -# expect(editor.getCursorBufferPosition()).toEqual [3, 12] -# -# describe "when the snippet spans multiple lines", -> -# beforeEach -> -# editor.update({autoIndent: true}) -# # editor.update() returns a Promise that never gets resolved, so we -# # need to return undefined to avoid a timeout in the spec. -# # TODO: Figure out why `editor.update({autoIndent: true})` never gets resolved. -# return -# -# it "places tab stops correctly", -> -# expect(editor.getSoftTabs()).toBeTruthy() -# editor.setCursorScreenPosition([2, Infinity]) -# editor.insertText ' t3' -# atom.commands.dispatch editorElement, 'snippets:expand' -# expect(editor.getCursorBufferPosition()).toEqual [3, 12] -# atom.commands.dispatch editorElement, 'snippets:next-tab-stop' -# expect(editor.getCursorBufferPosition()).toEqual [4, 4] -# -# it "indents the subsequent lines of the snippet based on the indent level before the snippet is inserted", -> -# editor.setCursorScreenPosition([2, Infinity]) -# editor.insertNewline() -# editor.insertText 't4b' -# atom.commands.dispatch editorElement, 'snippets:expand' -# -# expect(editor.lineTextForBufferRow(3)).toBe " = line 1 {" # 4 + 1 spaces (because the tab stop is invisible) -# expect(editor.lineTextForBufferRow(4)).toBe " line 2" -# expect(editor.lineTextForBufferRow(5)).toBe " }" -# expect(editor.getCursorBufferPosition()).toEqual [3, 4] -# -# it "does not change the relative positioning of the tab stops when inserted multiple times", -> -# editor.setCursorScreenPosition([2, Infinity]) -# editor.insertNewline() -# editor.insertText 't4' -# atom.commands.dispatch editorElement, 'snippets:expand' -# -# expect(editor.getSelectedBufferRange()).toEqual [[3, 9], [3, 10]] -# atom.commands.dispatch editorElement, 'snippets:next-tab-stop' -# expect(editor.getSelectedBufferRange()).toEqual [[4, 6], [4, 13]] -# -# editor.insertText 't4' -# atom.commands.dispatch editorElement, 'snippets:expand' -# -# expect(editor.getSelectedBufferRange()).toEqual [[4, 11], [4, 12]] -# atom.commands.dispatch editorElement, 'snippets:next-tab-stop' -# expect(editor.getSelectedBufferRange()).toEqual [[5, 8], [5, 15]] -# -# editor.setText('') # Clear editor -# editor.insertText 't4' -# atom.commands.dispatch editorElement, 'snippets:expand' -# -# expect(editor.getSelectedBufferRange()).toEqual [[0, 5], [0, 6]] -# atom.commands.dispatch editorElement, 'snippets:next-tab-stop' -# expect(editor.getSelectedBufferRange()).toEqual [[1, 2], [1, 9]] -# -# describe "when multiple snippets match the prefix", -> -# it "expands the snippet that is the longest match for the prefix", -> -# editor.insertText('t113') -# expect(editor.getCursorScreenPosition()).toEqual [0, 4] -# -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "t113 var quicksort = function () {" -# expect(editor.getCursorScreenPosition()).toEqual [0, 6] -# -# editor.undo() -# editor.undo() -# -# editor.insertText("tt1") -# expect(editor.getCursorScreenPosition()).toEqual [0, 3] -# -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "this is another testvar quicksort = function () {" -# expect(editor.getCursorScreenPosition()).toEqual [0, 20] -# -# editor.undo() -# editor.undo() -# -# editor.insertText("@t1") -# expect(editor.getCursorScreenPosition()).toEqual [0, 3] -# -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "@this is a testvar quicksort = function () {" -# expect(editor.getCursorScreenPosition()).toEqual [0, 15] -# -# describe "when the word preceding the cursor ends with a snippet prefix", -> -# it "inserts a tab as normal", -> -# editor.insertText("t1t1t1") -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "t1t1t1 var quicksort = function () {" -# -# describe "when the letters preceding the cursor don't match a snippet", -> -# it "inserts a tab as normal", -> -# editor.insertText("xxte") -# expect(editor.getCursorScreenPosition()).toEqual [0, 4] -# -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "xxte var quicksort = function () {" -# expect(editor.getCursorScreenPosition()).toEqual [0, 6] -# -# describe "when text is selected", -> -# it "inserts a tab as normal", -> -# editor.insertText("t1") -# editor.setSelectedBufferRange([[0, 0], [0, 2]]) -# -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe " t1var quicksort = function () {" -# expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 4]] -# -# describe "when a previous snippet expansion has just been undone", -> -# describe "when the tab stops appear in the middle of the snippet", -> -# 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] -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "first line" -# editor.undo() -# expect(editor.lineTextForBufferRow(0)).toBe "t6" -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "first line" -# -# describe "when the tab stops appear at the beginning and then the end of snippet", -> -# it "expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", -> -# editor.insertText 't6b\n' -# editor.setCursorBufferPosition [0, 3] -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "expanded" -# editor.undo() -# expect(editor.lineTextForBufferRow(0)).toBe "t6b" -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "expanded" -# expect(editor.getCursorBufferPosition()).toEqual([0, 0]) -# -# describe "when the tab stops appear at the end and then the beginning of snippet", -> -# it "expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", -> -# editor.insertText 't6c\n' -# editor.setCursorBufferPosition [0, 3] -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "expanded" -# editor.undo() -# expect(editor.lineTextForBufferRow(0)).toBe "t6c" -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "expanded" -# expect(editor.getCursorBufferPosition()).toEqual([0, 8]) -# -# describe "when the prefix contains non-word characters", -> -# it "selects the non-word characters as part of the prefix", -> -# editor.insertText("@unique") -# expect(editor.getCursorScreenPosition()).toEqual [0, 7] -# -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "@unique seevar quicksort = function () {" -# expect(editor.getCursorScreenPosition()).toEqual [0, 11] -# -# editor.setCursorBufferPosition [10, 0] -# editor.insertText("'@unique") -# -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(10)).toBe "'@unique see" -# expect(editor.getCursorScreenPosition()).toEqual [10, 12] -# -# it "does not select the whitespace before the prefix", -> -# editor.insertText("a; @unique") -# expect(editor.getCursorScreenPosition()).toEqual [0, 10] -# -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "a; @unique seevar quicksort = function () {" -# expect(editor.getCursorScreenPosition()).toEqual [0, 14] -# -# describe "when snippet contains tabstops with or without placeholder", -> -# it "should create two markers", -> -# editor.setCursorScreenPosition([0, 0]) -# editor.insertText('t8') -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" -# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" -# -# expect(editor.getSelectedBufferRange()).toEqual [[0, 17], [0, 21]] -# -# simulateTabKeyEvent() -# expect(editor.getSelectedBufferRange()).toEqual [[1, 20], [1, 20]] -# -# describe "when snippet contains multi-caret tabstops with or without placeholder", -> -# it "should create two markers", -> -# editor.setCursorScreenPosition([0, 0]) -# editor.insertText('t9') -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" -# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" -# editor.insertText('hello') -# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder hello" -# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder hellovar quicksort = function () {" -# -# it "terminates the snippet when cursors are destroyed", -> -# editor.setCursorScreenPosition([0, 0]) -# editor.insertText('t9b') -# simulateTabKeyEvent() -# editor.getCursors()[0].destroy() -# editor.getCursorBufferPosition() -# simulateTabKeyEvent() -# -# expect(editor.lineTextForBufferRow(1)).toEqual("without placeholder ") -# -# it "terminates the snippet expansion if a new cursor moves outside the bounds of the tab stops", -> -# editor.setCursorScreenPosition([0, 0]) -# editor.insertText('t9b') -# simulateTabKeyEvent() -# editor.insertText('test') -# -# editor.getCursors()[0].destroy() -# editor.moveDown() # this should destroy the previous expansion -# editor.moveToBeginningOfLine() -# -# # this should insert whitespace instead of going through tabstops of the previous destroyed snippet -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(2).indexOf(" second")).toBe 0 -# -# it "moves to the second tabstop after a multi-caret tabstop", -> -# editor.setCursorScreenPosition([0, 0]) -# editor.insertText('t9b') -# simulateTabKeyEvent() -# editor.insertText('line 1') -# -# simulateTabKeyEvent() -# editor.insertText('line 2') -# -# simulateTabKeyEvent() -# editor.insertText('line 3') -# -# expect(editor.lineTextForBufferRow(2).indexOf("line 2 ")).toBe -1 -# -# it "mirrors input properly when a tabstop's placeholder refers to another tabstop", -> -# editor.setText('t17') -# editor.setCursorScreenPosition([0, 3]) -# simulateTabKeyEvent() -# editor.insertText("foo") -# expect(editor.getText()).toBe "console.log('uh foo', foo);" -# simulateTabKeyEvent() -# editor.insertText("bar") -# expect(editor.getText()).toBe "console.log('bar', foo);" -# -# describe "when the snippet contains tab stops with transformations", -> -# it "transforms the text typed into the first tab stop before setting it in the transformed tab stop", -> -# editor.setText('t12') -# editor.setCursorScreenPosition([0, 3]) -# simulateTabKeyEvent() -# expect(editor.getText()).toBe("[b][/b]") -# editor.insertText('img src') -# expect(editor.getText()).toBe("[img src][/img]") -# -# it "bundles the transform mutations along with the original manual mutation for the purposes of undo and redo", -> -# editor.setText('t12') -# editor.setCursorScreenPosition([0, 3]) -# simulateTabKeyEvent() -# editor.insertText('i') -# expect(editor.getText()).toBe("[i][/i]") -# -# editor.insertText('mg src') -# expect(editor.getText()).toBe("[img src][/img]") -# -# editor.undo() -# expect(editor.getText()).toBe("[i][/i]") -# -# editor.redo() -# expect(editor.getText()).toBe("[img src][/img]") -# -# it "can pick the right insertion to use as the primary even if a transformed insertion occurs first in the snippet", -> -# editor.setText('t16') -# editor.setCursorScreenPosition([0, 3]) -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe("& Q & q") -# expect(editor.getCursorBufferPosition()).toEqual([0, 7]) -# -# editor.insertText('rst') -# expect(editor.lineTextForBufferRow(0)).toBe("& RST & rst") -# -# it "silently ignores a tab stop without a non-transformed insertion to use as the primary", -> -# editor.setText('t15') -# editor.setCursorScreenPosition([0, 3]) -# simulateTabKeyEvent() -# editor.insertText('a') -# expect(editor.lineTextForBufferRow(0)).toBe(" & a") -# expect(editor.getCursorBufferPosition()).toEqual([0, 4]) -# -# describe "when the snippet contains mirrored tab stops and tab stops with transformations", -> -# it "adds cursors for the mirrors but not the transformations", -> -# editor.setText('t13') -# editor.setCursorScreenPosition([0, 3]) -# simulateTabKeyEvent() -# expect(editor.getCursors().length).toBe(2) -# expect(editor.getText()).toBe """ -# placeholder -# PLACEHOLDER -# -# """ -# -# editor.insertText('foo') -# -# expect(editor.getText()).toBe """ -# foo -# FOO -# foo -# """ -# -# describe "when the snippet contains multiple tab stops, some with transformations and some without", -> -# it "does not get confused", -> -# editor.setText('t14') -# editor.setCursorScreenPosition([0, 3]) -# simulateTabKeyEvent() -# expect(editor.getCursors().length).toBe(2) -# expect(editor.getText()).toBe "placeholder PLACEHOLDER ANOTHER another " -# simulateTabKeyEvent() -# expect(editor.getCursors().length).toBe(2) -# editor.insertText('FOO') -# expect(editor.getText()).toBe """ -# placeholder PLACEHOLDER FOO foo FOO -# """ -# -# describe "when the snippet has a transformed tab stop such that it is possible to move the cursor between the ordinary tab stop and its transformed version without an intermediate step", -> -# it "terminates the snippet upon such a cursor move", -> -# editor.setText('t18') -# editor.setCursorScreenPosition([0, 3]) -# simulateTabKeyEvent() -# expect(editor.getText()).toBe("// \n// ") -# expect(editor.getCursorBufferPosition()).toEqual [0, 3] -# editor.insertText('wat') -# expect(editor.getText()).toBe("// wat\n// ===") -# # Move the cursor down one line, then up one line. This puts the cursor -# # back in its previous position, but the snippet should no longer be -# # active, so when we type more text, it should not be mirrored. -# editor.setCursorScreenPosition([1, 6]) -# editor.setCursorScreenPosition([0, 6]) -# editor.insertText('wat') -# expect(editor.getText()).toBe("// watwat\n// ===") -# -# -# describe "when the snippet contains tab stops with an index >= 10", -> -# it "parses and orders the indices correctly", -> -# editor.setText('t10') -# editor.setCursorScreenPosition([0, 3]) -# simulateTabKeyEvent() -# expect(editor.getText()).toBe "hello large indices" -# expect(editor.getCursorBufferPosition()).toEqual [0, 19] -# simulateTabKeyEvent() -# expect(editor.getCursorBufferPosition()).toEqual [0, 5] -# simulateTabKeyEvent() -# expect(editor.getSelectedBufferRange()).toEqual [[0, 6], [0, 11]] -# -# describe "when there are multiple cursors", -> -# describe "when the cursors share a common snippet prefix", -> -# it "expands the snippet for all cursors and allows simultaneous editing", -> -# editor.insertText('t9') -# editor.setCursorBufferPosition([12, 2]) -# editor.insertText(' t9') -# editor.addCursorAtBufferPosition([0, 2]) -# simulateTabKeyEvent() -# -# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder test" -# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder var quicksort = function () {" -# expect(editor.lineTextForBufferRow(13)).toBe "}; with placeholder test" -# expect(editor.lineTextForBufferRow(14)).toBe "without placeholder " -# -# editor.insertText('hello') -# expect(editor.lineTextForBufferRow(0)).toBe "with placeholder hello" -# expect(editor.lineTextForBufferRow(1)).toBe "without placeholder hellovar quicksort = function () {" -# expect(editor.lineTextForBufferRow(13)).toBe "}; with placeholder hello" -# expect(editor.lineTextForBufferRow(14)).toBe "without placeholder hello" -# -# it "applies transformations identically to single-expansion mode", -> -# editor.setText('t14\nt14') -# editor.setCursorBufferPosition([1, 3]) -# editor.addCursorAtBufferPosition([0, 3]) -# simulateTabKeyEvent() -# -# expect(editor.lineTextForBufferRow(0)).toBe "placeholder PLACEHOLDER ANOTHER another " -# expect(editor.lineTextForBufferRow(1)).toBe "placeholder PLACEHOLDER ANOTHER another " -# -# editor.insertText "testing" -# -# expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing ANOTHER another " -# expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing ANOTHER another " -# -# simulateTabKeyEvent() -# editor.insertText "AGAIN" -# -# expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing AGAIN again AGAIN" -# expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing AGAIN again AGAIN" -# -# it "bundles transform-induced mutations into a single history entry along with their triggering edit, even across multiple snippets", -> -# editor.setText('t14\nt14') -# editor.setCursorBufferPosition([1, 3]) -# editor.addCursorAtBufferPosition([0, 3]) -# simulateTabKeyEvent() -# -# expect(editor.lineTextForBufferRow(0)).toBe "placeholder PLACEHOLDER ANOTHER another " -# expect(editor.lineTextForBufferRow(1)).toBe "placeholder PLACEHOLDER ANOTHER another " -# -# editor.insertText "testing" -# -# expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing ANOTHER another " -# expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing ANOTHER another " -# -# simulateTabKeyEvent() -# editor.insertText "AGAIN" -# -# expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing AGAIN again AGAIN" -# expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing AGAIN again AGAIN" -# -# editor.undo() -# expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing ANOTHER another " -# expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing ANOTHER another " -# -# editor.undo() -# expect(editor.lineTextForBufferRow(0)).toBe "placeholder PLACEHOLDER ANOTHER another " -# expect(editor.lineTextForBufferRow(1)).toBe "placeholder PLACEHOLDER ANOTHER another " -# -# editor.redo() -# expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing ANOTHER another " -# expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing ANOTHER another " -# -# editor.redo() -# expect(editor.lineTextForBufferRow(0)).toBe "testing TESTING testing AGAIN again AGAIN" -# expect(editor.lineTextForBufferRow(1)).toBe "testing TESTING testing AGAIN again AGAIN" -# -# describe "when there are many tabstops", -> -# it "moves the cursors between the tab stops for their corresponding snippet when tab and shift-tab are pressed", -> -# editor.addCursorAtBufferPosition([7, 5]) -# editor.addCursorAtBufferPosition([12, 2]) -# editor.insertText('t11') -# simulateTabKeyEvent() -# -# cursors = editor.getCursors() -# expect(cursors.length).toEqual 3 -# -# expect(cursors[0].getBufferPosition()).toEqual [0, 3] -# expect(cursors[1].getBufferPosition()).toEqual [7, 8] -# expect(cursors[2].getBufferPosition()).toEqual [12, 5] -# expect(cursors[0].selection.isEmpty()).toBe true -# expect(cursors[1].selection.isEmpty()).toBe true -# expect(cursors[2].selection.isEmpty()).toBe true -# -# simulateTabKeyEvent() -# expect(cursors[0].getBufferPosition()).toEqual [0, 7] -# expect(cursors[1].getBufferPosition()).toEqual [7, 12] -# expect(cursors[2].getBufferPosition()).toEqual [12, 9] -# expect(cursors[0].selection.isEmpty()).toBe false -# expect(cursors[1].selection.isEmpty()).toBe false -# expect(cursors[2].selection.isEmpty()).toBe false -# expect(cursors[0].selection.getText()).toEqual 'two' -# expect(cursors[1].selection.getText()).toEqual 'two' -# expect(cursors[2].selection.getText()).toEqual 'two' -# -# simulateTabKeyEvent() -# expect(cursors[0].getBufferPosition()).toEqual [0, 13] -# expect(cursors[1].getBufferPosition()).toEqual [7, 18] -# expect(cursors[2].getBufferPosition()).toEqual [12, 15] -# expect(cursors[0].selection.isEmpty()).toBe true -# expect(cursors[1].selection.isEmpty()).toBe true -# expect(cursors[2].selection.isEmpty()).toBe true -# -# simulateTabKeyEvent() -# expect(cursors[0].getBufferPosition()).toEqual [0, 0] -# expect(cursors[1].getBufferPosition()).toEqual [7, 5] -# expect(cursors[2].getBufferPosition()).toEqual [12, 2] -# expect(cursors[0].selection.isEmpty()).toBe true -# expect(cursors[1].selection.isEmpty()).toBe true -# expect(cursors[2].selection.isEmpty()).toBe true -# -# describe "when the cursors do not share common snippet prefixes", -> -# it "inserts tabs as normal", -> -# editor.insertText('t9') -# editor.setCursorBufferPosition([12, 2]) -# editor.insertText(' t8') -# editor.addCursorAtBufferPosition([0, 2]) -# simulateTabKeyEvent() -# expect(editor.lineTextForBufferRow(0)).toBe "t9 var quicksort = function () {" -# expect(editor.lineTextForBufferRow(12)).toBe "}; t8 " -# -# describe "when a snippet is triggered within an existing snippet expansion", -> -# it "ignores the snippet expansion and goes to the next tab stop", -> -# editor.addCursorAtBufferPosition([7, 5]) -# editor.addCursorAtBufferPosition([12, 2]) -# editor.insertText('t11') -# simulateTabKeyEvent() -# simulateTabKeyEvent() -# -# editor.insertText('t1') -# simulateTabKeyEvent() -# -# cursors = editor.getCursors() -# expect(cursors.length).toEqual 3 -# -# expect(cursors[0].getBufferPosition()).toEqual [0, 12] -# expect(cursors[1].getBufferPosition()).toEqual [7, 17] -# expect(cursors[2].getBufferPosition()).toEqual [12, 14] -# expect(cursors[0].selection.isEmpty()).toBe true -# expect(cursors[1].selection.isEmpty()).toBe true -# expect(cursors[2].selection.isEmpty()).toBe true -# expect(editor.lineTextForBufferRow(0)).toBe "one t1 threevar quicksort = function () {" -# expect(editor.lineTextForBufferRow(7)).toBe " }one t1 three" -# expect(editor.lineTextForBufferRow(12)).toBe "};one t1 three" -# -# describe "when the editor is not a pane item (regression)", -> -# it "handles tab stops correctly", -> -# editor = new TextEditor() -# atom.grammars.assignLanguageMode(editor, 'source.js') -# editorElement = editor.getElement() -# -# editor.insertText('t2') -# simulateTabKeyEvent() -# editor.insertText('ABC') -# expect(editor.getText()).toContain('go here first:(ABC)') -# -# editor.undo() -# editor.undo() -# expect(editor.getText()).toBe('t2') -# simulateTabKeyEvent() -# editor.insertText('ABC') -# expect(editor.getText()).toContain('go here first:(ABC)') -# -# describe "when atom://.atom/snippets is opened", -> -# it "opens ~/.atom/snippets.cson", -> -# jasmine.unspy(Snippets, 'getUserSnippetsPath') -# atom.workspace.destroyActivePaneItem() -# configDirPath = temp.mkdirSync('atom-config-dir-') -# spyOn(atom, 'getConfigDirPath').andReturn configDirPath -# atom.workspace.open('atom://.atom/snippets') -# -# waitsFor -> -# atom.workspace.getActiveTextEditor()? -# -# runs -> -# expect(atom.workspace.getActiveTextEditor().getURI()).toBe path.join(configDirPath, 'snippets.cson') -# -# describe "snippet insertion API", -> -# it "will automatically parse snippet definition and replace selection", -> -# editor.setSelectedBufferRange([[0, 4], [0, 13]]) -# Snippets.insert("hello ${1:world}", editor) -# -# expect(editor.lineTextForBufferRow(0)).toBe "var hello world = function () {" -# expect(editor.getSelectedBufferRange()).toEqual [[0, 10], [0, 15]] -# -# describe "when the 'snippets:available' command is triggered", -> -# availableSnippetsView = null -# -# beforeEach -> -# Snippets.add __filename, -# ".source.js": -# "test": -# prefix: "test" -# body: "${1:Test pass you will}, young " -# -# "challenge": -# prefix: "chal" -# body: "$1: ${2:To pass this challenge}" -# -# delete Snippets.availableSnippetsView -# -# atom.commands.dispatch(editorElement, "snippets:available") -# -# waitsFor -> -# atom.workspace.getModalPanels().length is 1 -# -# runs -> -# availableSnippetsView = atom.workspace.getModalPanels()[0].getItem() -# -# it "renders a select list of all available snippets", -> -# expect(availableSnippetsView.selectListView.getSelectedItem().prefix).toBe 'test' -# expect(availableSnippetsView.selectListView.getSelectedItem().name).toBe 'test' -# expect(availableSnippetsView.selectListView.getSelectedItem().bodyText).toBe '${1:Test pass you will}, young ' -# -# availableSnippetsView.selectListView.selectNext() -# -# expect(availableSnippetsView.selectListView.getSelectedItem().prefix).toBe 'chal' -# expect(availableSnippetsView.selectListView.getSelectedItem().name).toBe 'challenge' -# expect(availableSnippetsView.selectListView.getSelectedItem().bodyText).toBe '$1: ${2:To pass this challenge}' -# -# it "writes the selected snippet to the editor as snippet", -> -# availableSnippetsView.selectListView.confirmSelection() -# -# expect(editor.getCursorScreenPosition()).toEqual [0, 18] -# expect(editor.getSelectedText()).toBe 'Test pass you will' -# expect(editor.lineTextForBufferRow(0)).toBe 'Test pass you will, young var quicksort = function () {' -# -# it "closes the dialog when triggered again", -> -# atom.commands.dispatch availableSnippetsView.selectListView.refs.queryEditor.element, 'snippets:available' -# expect(atom.workspace.getModalPanels().length).toBe 0 From f3c5de687da0cbf5423e017c0804368bccf29cda Mon Sep 17 00:00:00 2001 From: Benjamin Gray Date: Sun, 29 Dec 2019 23:48:13 +1000 Subject: [PATCH 4/6] clean up single line strings --- spec/body-parser-spec.js | 6 +++--- spec/snippets-spec.js | 28 +++++++--------------------- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/spec/body-parser-spec.js b/spec/body-parser-spec.js index a5c8a54..35492de 100644 --- a/spec/body-parser-spec.js +++ b/spec/body-parser-spec.js @@ -116,7 +116,7 @@ the quick brown $1fox \${2:jumped \${3:over} it("parses a snippet with transformations and mirrors", () => { - const bodyTree = BodyParser.parse("${1:placeholder}\n\${1/(.)/\\u$1/}\n$1"); + const bodyTree = BodyParser.parse("${1:placeholder}\n${1/(.)/\\u$1/}\n$1"); expect(bodyTree).toEqual([ {index: 1, content: ['placeholder']}, '\n', @@ -137,7 +137,7 @@ the quick brown $1fox \${2:jumped \${3:over} }); it("parses a snippet with a format string and case-control flags", () => { - const bodyTree = BodyParser.parse("<\${1:p}>$0"); + const bodyTree = BodyParser.parse("<${1:p}>$0"); expect(bodyTree).toEqual([ '<', {index: 1, content: ['p']}, @@ -187,7 +187,7 @@ the quick brown $1fox \${2:jumped \${3:over} }); it("parses a snippet with a placeholder that mirrors another tab stop's content", () => { - const bodyTree = BodyParser.parse("$4console.\${3:log}('\${2:$1}', $1);$0"); + const bodyTree = BodyParser.parse("$4console.${3:log}('${2:$1}', $1);$0"); expect(bodyTree).toEqual([ {index: 4, content: []}, 'console.', diff --git a/spec/snippets-spec.js b/spec/snippets-spec.js index aed512c..6899447 100644 --- a/spec/snippets-spec.js +++ b/spec/snippets-spec.js @@ -240,9 +240,7 @@ third tabstop $3\ "large indices": { prefix: "t10", - body: `\ -hello\${10} \${11:large} indices\${1}\ -` + body: "hello${10} ${11:large} indices${1}" }, "no body": { @@ -256,40 +254,28 @@ hello\${10} \${11:large} indices\${1}\ "many tabstops": { prefix: "t11", - body: `\ -$0one\${1} \${2:two} three\${3}\ -` + body: "$0one${1} ${2:two} three${3}" }, "simple transform": { prefix: "t12", - body: `\ -[\${1:b}][/\${1/[ ]+.*$//}]\ -` + body: "[${1:b}][/${1/[ ]+.*$//}]" }, "transform with non-transforming mirrors": { prefix: "t13", - body: `\ -\${1:placeholder}\n\${1/(.)/\\u$1/}\n$1\ -` + body: "${1:placeholder}\n${1/(.)/\\u$1/}\n$1" }, "multiple tab stops, some with transforms and some without": { prefix: "t14", - body: `\ -\${1:placeholder} \${1/(.)/\\u$1/} $1 \${2:ANOTHER} \${2/^(.*)$/\\L$1/} $2\ -` + body: "${1:placeholder} ${1/(.)/\\u$1/} $1 ${2:ANOTHER} ${2/^(.*)$/\\L$1/} $2" }, "has a transformed tab stop without a corresponding ordinary tab stop": { prefix: 't15', - body: `\ -\${1/(.)/\\u$1/} & $2\ -` + body: "${1/(.)/\\u$1/} & $2" }, "has a transformed tab stop that occurs before the corresponding ordinary tab stop": { prefix: 't16', - body: `\ -& \${1/(.)/\\u$1/} & \${1:q}\ -` + body: "& ${1/(.)/\\u$1/} & ${1:q}" }, "has a placeholder that mirrors another tab stop's content": { prefix: 't17', From 72e9a52f5ac9ebad6eebac48b4df87bc67b85631 Mon Sep 17 00:00:00 2001 From: Benjamin Gray Date: Sun, 29 Dec 2019 23:48:52 +1000 Subject: [PATCH 5/6] don't return when no return expected --- spec/snippet-loading-spec.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/snippet-loading-spec.js b/spec/snippet-loading-spec.js index 0772ce1..e799e1c 100644 --- a/spec/snippet-loading-spec.js +++ b/spec/snippet-loading-spec.js @@ -20,7 +20,9 @@ describe("Snippet Loading", () => { afterEach(() => { waitsForPromise(() => Promise.resolve(atom.packages.deactivatePackages('snippets'))); - runs(() => jasmine.unspy(atom.packages, 'getLoadedPackages')); + runs(() => { + jasmine.unspy(atom.packages, 'getLoadedPackages'); + }); }); const activateSnippetsPackage = () => { @@ -169,7 +171,9 @@ describe("Snippet Loading", () => { return snippet && snippet.body === 'bar2'; }); - runs(() => fs.writeFileSync(path.join(configDirPath, 'snippets.json'), "")); + runs(() => { + fs.writeFileSync(path.join(configDirPath, 'snippets.json'), ""); + }); waitsFor("snippets to be removed", () => !snippetsService.snippetsForScopes(['.foo'])['foo']); }); From a4580d67d99024593525b5ad389f0ff10c487ace Mon Sep 17 00:00:00 2001 From: Benjamin Gray Date: Thu, 2 Jan 2020 12:50:37 +1000 Subject: [PATCH 6/6] fix config restore --- spec/snippet-loading-spec.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/snippet-loading-spec.js b/spec/snippet-loading-spec.js index e799e1c..1b39ee4 100644 --- a/spec/snippet-loading-spec.js +++ b/spec/snippet-loading-spec.js @@ -267,9 +267,7 @@ describe("Snippet Loading", () => { const allSnippets = snippetsService.getUnparsedSnippets(); const scopedSnippet = allSnippets.find(s => s.selectorString === '.package-with-snippets-unique-scope'); expect(scopedSnippet).not.toBe(undefined); - - //TODO: This doesn't look right. originalConfig should be restored - originalConfig = atom.config.get('core.packagesWithSnippetsDisabled'); + atom.config.set('core.packagesWithSnippetsDisabled', originalConfig); }); });