From 85fa483ae12d539469930fb0d8114376b282fe4e Mon Sep 17 00:00:00 2001 From: Benjamin Gray Date: Thu, 5 Dec 2019 14:12:09 +1000 Subject: [PATCH 1/5] Initial decaf --- spec/body-parser-spec.js | 271 ++++++++ spec/snippet-loading-spec.js | 303 +++++++++ spec/snippets-spec.js | 1128 ++++++++++++++++++++++++++++++++++ 3 files changed, 1702 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 00000000..475dadd4 --- /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 00000000..35ea4b41 --- /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 00000000..107754fd --- /dev/null +++ b/spec/snippets-spec.js @@ -0,0 +1,1128 @@ +/* + * 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}\ +` + }, + + "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(() => editor.update({autoIndent: true})); + + 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", () => 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 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 d8ca22deaf7ceeb11628d7ecda0e52fd1002fd05 Mon Sep 17 00:00:00 2001 From: Benjamin Gray Date: Thu, 5 Dec 2019 14:26:10 +1000 Subject: [PATCH 2/5] clean up body-parser-spec decaf --- spec/body-parser-spec.js | 138 ++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 82 deletions(-) diff --git a/spec/body-parser-spec.js b/spec/body-parser-spec.js index 475dadd4..75623f2c 100644 --- a/spec/body-parser-spec.js +++ b/spec/body-parser-spec.js @@ -1,19 +1,12 @@ -/* - * 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\ -` +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}\n" + + "}the ${4:lazy} dog" ); - - return expect(bodyTree).toEqual([ + expect(bodyTree).toEqual([ "the quick brown ", {index: 1, content: []}, "fox ", @@ -29,30 +22,26 @@ 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}}\ -` + 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}}" ); - - return expect(bodyTree).toEqual([ + expect(bodyTree).toEqual([ "module ", { "index": 1, "content": ["ActiveRecord::", ""] } ]); -}); + }); - it("skips escaped tabstops", function() { - const bodyTree = BodyParser.parse(`\ -snippet $1 escaped \\$2 \\\\$3\ -` + it("skips escaped tabstops", () => { + const bodyTree = BodyParser.parse( + "snippet $1 escaped \\$2 \\\\$3" ); - - return expect(bodyTree).toEqual([ + expect(bodyTree).toEqual([ "snippet ", { index: 1, @@ -64,29 +53,26 @@ snippet $1 escaped \\$2 \\\\$3\ content: [] } ]); -}); + }); - it("includes escaped right-braces", function() { - const bodyTree = BodyParser.parse(`\ -snippet \${1:{\\}}\ -` + it("includes escaped right-braces", () => { + const bodyTree = BodyParser.parse( + "snippet ${1:{\\}}" ); - - return expect(bodyTree).toEqual([ + expect(bodyTree).toEqual([ "snippet ", { index: 1, content: ["{}"] } ]); -}); + }); - it("parses a snippet with transformations", function() { - const bodyTree = BodyParser.parse(`\ -<\${1:p}>$0\ -` + it("parses a snippet with transformations", () => { + const bodyTree = BodyParser.parse( + "<${1:p}>$0" ); - return expect(bodyTree).toEqual([ + expect(bodyTree).toEqual([ '<', {index: 1, content: ['p']}, '>', @@ -95,15 +81,13 @@ 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\ -` + 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" ); - - return expect(bodyTree).toEqual([ + expect(bodyTree).toEqual([ {index: 1, content: ['placeholder']}, ' ', { @@ -139,13 +123,11 @@ snippet \${1:{\\}}\ }); - it("parses a snippet with transformations and mirrors", function() { - const bodyTree = BodyParser.parse(`\ -\${1:placeholder}\n\${1/(.)/\\u$1/}\n$1\ -` + it("parses a snippet with transformations and mirrors", () => { + const bodyTree = BodyParser.parse( + "${1:placeholder}\n\${1/(.)/\\u$1/}\n$1" ); - - return expect(bodyTree).toEqual([ + expect(bodyTree).toEqual([ {index: 1, content: ['placeholder']}, '\n', { @@ -162,15 +144,13 @@ 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\ -` + it("parses a snippet with a format string and case-control flags", () => { + const bodyTree = BodyParser.parse( + "<${1:p}>$0" ); - - return expect(bodyTree).toEqual([ + expect(bodyTree).toEqual([ '<', {index: 1, content: ['p']}, '>', @@ -190,17 +170,15 @@ 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\ -` + const bodyTree = BodyParser.parse( + "<${1:p}>$0" ); - - return expect(bodyTree).toEqual([ + expect(bodyTree).toEqual([ '<', {index: 1, content: ['p']}, '>', @@ -220,15 +198,13 @@ 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\ -` + 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" ); - - return expect(bodyTree).toEqual([ + expect(bodyTree).toEqual([ {index: 4, content: []}, 'console.', {index: 3, content: ['log']}, @@ -243,15 +219,13 @@ $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\ -` + 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" ); - - return expect(bodyTree).toEqual([ + expect(bodyTree).toEqual([ {index: 4, content: []}, 'console.', {index: 3, content: ['log']}, @@ -267,5 +241,5 @@ $4console.\${3:log}('\${2:uh $1}', $1);$0\ ');', {index: 0, content: []} ]); -}); + }); }); From 9d3f0aa3a6b55bcbe1c52170abd7c7e6f884208b Mon Sep 17 00:00:00 2001 From: Benjamin Gray Date: Thu, 5 Dec 2019 15:44:23 +1000 Subject: [PATCH 3/5] clean up snippet-loading-spec decaf --- spec/snippet-loading-spec.js | 233 ++++++++++++++++++----------------- 1 file changed, 120 insertions(+), 113 deletions(-) diff --git a/spec/snippet-loading-spec.js b/spec/snippet-loading-spec.js index 35ea4b41..2781f13d 100644 --- a/spec/snippet-loading-spec.js +++ b/spec/snippet-loading-spec.js @@ -1,47 +1,39 @@ -/* - * 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'); } + if (atom.notifications !== undefined) { 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 +46,13 @@ 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'); @@ -80,53 +71,54 @@ describe("Snippet Loading", function() { 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'); + expect(snippet.leftLabelHTML).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; - - 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("::onDidLoadSnippets(callback)", () => { + it("invokes listeners when all snippets are loaded", () => { + let loadedCallback = null; + waitsFor("package to activate", done => { + atom.packages.activatePackage("snippets").then(({mainModule}) => { + loadedCallback = jasmine.createSpy('onDidLoadSnippets callback'); + mainModule.onDidLoadSnippets(loadedCallback); + done(); + }); + }); + 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 +130,25 @@ 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() { + waitsFor(() => { + snippet = snippetsService.snippetsForScopes(['.foo'])['foo']; + return snippet; + }); + 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 +158,24 @@ 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": @@ -189,42 +186,48 @@ describe("Snippet Loading", function() { return 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 snippet; + }); - 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; + }); }); - })); + }); }); - it("notifies the user when the user snippets file cannot be loaded", function() { + it("notifies the user when the user snippets file cannot be loaded", () => { fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), `\ ".junk":::\ ` @@ -232,57 +235,61 @@ describe("Snippet Loading", function() { 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() { - let originalConfig = atom.config.get('core.packagesWithSnippetsDisabled'); + it("still includes a disabled package's snippets in the list of unparsed snippets", () => { + const 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'); + + // NOTE: Original seems to have been incorrectly reassigning to originalConfig + atom.config.set('core.packagesWithSnippetsDisabled', originalConfig); }); }); - 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 +303,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); }); }); }); From 9562b9bce28872bf4530aee97e659178c51ca61c Mon Sep 17 00:00:00 2001 From: Benjamin Gray Date: Thu, 5 Dec 2019 16:52:52 +1000 Subject: [PATCH 4/5] clean up snippets-spec decaf --- spec/snippets-spec.js | 1214 +++++++++++++++++++++-------------------- 1 file changed, 617 insertions(+), 597 deletions(-) diff --git a/spec/snippets-spec.js b/spec/snippets-spec.js index 107754fd..345e1b9d 100644 --- a/spec/snippets-spec.js +++ b/spec/snippets-spec.js @@ -1,26 +1,18 @@ -/* - * 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 = {}) => { 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(''); @@ -30,27 +22,27 @@ describe("Snippets extension", function() { 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'))); - describe("provideSnippets interface", function() { + describe("provideSnippets interface", () => { let snippetsInterface = null; 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 +50,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,206 +89,190 @@ describe("Snippets extension", function() { 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" - } - }, + expect(snippets.getSnippets(editor)).toEqual({}); + }); - '.source.js .nope': { - "some snippet": { - prefix: "t1", - body: null + 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 + } } - } - } - )); + }); + }); - 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}' - }, - - "caused problems with undo": { - prefix: "t6", - body: `\ + }, + "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}\ + \${2:placeholder ending second line}\ ` - }, - - "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" - }, - - "number body": { - prefix: "bad2", - body: 100 - }, - - "many tabstops": { - prefix: "t11", - body: `\ + }, + "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: `\ + }, + "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"); @@ -323,8 +301,7 @@ $0one\${1} \${2:two} three\${3}\ body: "new snippet" } } - } - ); + }); editor.setText(""); editor.insertText("t1"); @@ -332,43 +309,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]); - }); + 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() { + 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 () {"); - 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'); @@ -401,156 +380,178 @@ $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(); + 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]]); + }); + }); - editor.moveUp(); - editor.moveLeft(); - 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(); - expect(editor.lineTextForBufferRow(2)).toBe("go here next:( ) and finally go here:()"); - expect(editor.getCursorBufferPosition()).toEqual([2, 16]); + expect(editor.lineTextForBufferRow(0)).toBe("something strange"); + expect(editor.getCursorBufferPosition()).toEqual([0, 10]); - // 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(); + 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(() => editor.update({autoIndent: true})); + describe("when the snippet spans multiple lines", () => { + beforeEach(() => { + editor.update({autoIndent: true}); + }); - 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'); @@ -559,10 +560,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'); @@ -585,77 +586,87 @@ $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]]); - })); + 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", () => 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 a previous snippet expansion has just been undone", () => { + it("expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", () => { + editor.insertText('t6\n'); + editor.setCursorBufferPosition([0, 2]); + 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 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]); @@ -668,34 +679,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(); @@ -703,21 +716,20 @@ $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(); editor.getCursors()[0].destroy(); 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(); @@ -729,10 +741,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(); @@ -744,10 +756,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(); @@ -755,21 +767,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(); @@ -783,10 +795,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(); @@ -794,89 +806,82 @@ $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(`\ -placeholder -PLACEHOLDER -\ -` - ); + 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\nPLACEHOLDER\n"); + editor.insertText('foo'); + expect(editor.getText()).toBe("foo\nFOO\nfoo"); + }); + }); - editor.insertText('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"); + }); + }); - return expect(editor.getText()).toBe(`\ -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 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 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'); @@ -892,10 +897,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]); @@ -913,10 +918,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]); @@ -950,132 +955,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); + }); }); + }); - 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 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(); 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].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, 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": { @@ -1088,8 +1107,7 @@ placeholder PLACEHOLDER FOO foo FOO\ body: "$1: ${2:To pass this challenge}" } } - } - ); + }); delete Snippets.availableSnippetsView; @@ -1097,10 +1115,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 '); @@ -1109,20 +1129,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 aa1c015f3835ecbe6545ae44cf85eeced5463906 Mon Sep 17 00:00:00 2001 From: Benjamin Gray Date: Thu, 5 Dec 2019 17:01:44 +1000 Subject: [PATCH 5/5] remove old tests --- spec/body-parser-spec.coffee | 243 ------- spec/snippet-loading-spec.coffee | 268 -------- spec/snippets-spec.coffee | 1035 ------------------------------ 3 files changed, 1546 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 2f8a28e7..00000000 --- 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 12a6c2aa..00000000 --- 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 1439bffe..00000000 --- a/spec/snippets-spec.coffee +++ /dev/null @@ -1,1035 +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} - """ - - "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}) - - 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", -> - 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 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