diff --git a/.coffeelintignore b/.coffeelintignore
deleted file mode 100644
index 1db51fed..00000000
--- a/.coffeelintignore
+++ /dev/null
@@ -1 +0,0 @@
-spec/fixtures
diff --git a/available-snippets-view.js b/available-snippets-view.js
new file mode 100644
index 00000000..fc476d6b
--- /dev/null
+++ b/available-snippets-view.js
@@ -0,0 +1,43 @@
+const SelectListView = require('atom-select-list')
+
+module.exports = class AvailableSnippetsView extends SelectListView {
+ constructor (snippets, editor) {
+ super({
+ items: Object.entries(snippets.snippetsByScopes()
+ .getPropertyValue(editor.getRootScopeDescriptor().getScopeChain())),
+ filterKeyForItem: ([name, { prefix }]) => prefix + name,
+ elementForItem: ([name, { prefix, description }]) => {
+ const li = document.createElement('li')
+ li.classList.add('two-lines')
+
+ const primaryLine = li.appendChild(document.createElement('div'))
+ primaryLine.classList.add('primary-line')
+ primaryLine.textContent = prefix
+
+ const secondaryLine = li.appendChild(document.createElement('div'))
+ secondaryLine.classList.add('secondary-line')
+ // TODO: Nullish coalescing operator
+ secondaryLine.textContent = description != null ? description : name
+
+ return li
+ },
+ emptyMessage: 'No snippets defined for this Grammar.',
+ itemsClassList: ['available-snippets'],
+ didConfirmSelection: ([, { body }]) => {
+ this.destroy()
+ editor.getCursors().forEach(cursor =>
+ snippets.parse(body).expand({ editor, cursor }))
+ },
+ didConfirmEmptySelection: () => this.destroy(),
+ didCancelSelection: () => this.destroy()
+ })
+
+ const panel = atom.workspace.addModalPanel({ item: this })
+ this.disposables.add(
+ // Register cleanup disposables to be called on desctruction
+ { dispose: () => document.activeElement.focus },
+ { dispose: () => { panel.destroy() } })
+
+ this.focus()
+ }
+}
diff --git a/babel.config.json b/babel.config.json
new file mode 100644
index 00000000..f0525c14
--- /dev/null
+++ b/babel.config.json
@@ -0,0 +1,6 @@
+{
+ "plugins": [
+ "@babel/plugin-proposal-class-properties",
+ "@babel/plugin-proposal-private-methods"
+ ]
+}
diff --git a/coffeelint.json b/coffeelint.json
deleted file mode 100644
index a5dd715e..00000000
--- a/coffeelint.json
+++ /dev/null
@@ -1,37 +0,0 @@
-{
- "max_line_length": {
- "level": "ignore"
- },
- "no_empty_param_list": {
- "level": "error"
- },
- "arrow_spacing": {
- "level": "error"
- },
- "no_interpolation_in_single_quotes": {
- "level": "error"
- },
- "no_debugger": {
- "level": "error"
- },
- "prefer_english_operator": {
- "level": "error"
- },
- "colon_assignment_spacing": {
- "spacing": {
- "left": 0,
- "right": 1
- },
- "level": "error"
- },
- "braces_spacing": {
- "spaces": 0,
- "level": "error"
- },
- "spacing_after_comma": {
- "level": "error"
- },
- "no_stand_alone_at": {
- "level": "error"
- }
-}
diff --git a/expression/babel.config.json b/expression/babel.config.json
new file mode 100644
index 00000000..f0525c14
--- /dev/null
+++ b/expression/babel.config.json
@@ -0,0 +1,6 @@
+{
+ "plugins": [
+ "@babel/plugin-proposal-class-properties",
+ "@babel/plugin-proposal-private-methods"
+ ]
+}
diff --git a/expression/choice.js b/expression/choice.js
new file mode 100644
index 00000000..cc361406
--- /dev/null
+++ b/expression/choice.js
@@ -0,0 +1,60 @@
+const { CompositeDisposable } = require('atom')
+
+const Expression = require('./expression')
+
+module.exports = class Choice extends Expression {
+ constructor (identifier, choices) {
+ super(identifier)
+
+ this.choices = choices
+ // The "first" and therefore default values is last in the list so that
+ // choice cycling starts from the second choice
+ this.default = choices[choices.length - 1]
+ }
+
+ activate (editor, cursor, stop, mirror) {
+ super.activate(editor, cursor, stop, mirror)
+ // Don't bother if a mirror, the marker won't get iterated over
+ if (mirror === stop) {
+ const disposables = new CompositeDisposable()
+
+ const target = 'atom-text-editor:not([mini])'
+ const cycle = `snippets:next-choice-${stop.id}`
+
+ const choices = {
+ choices: this.choices,
+ iterator: this.choices.values(),
+ next () {
+ const iteration = this.iterator.next()
+ const { value } = iteration.done
+ ? (this.iterator = this.choices.values()).next()
+ : iteration
+ editor.getBuffer().setTextInRange(stop.getBufferRange(), value)
+ cursor.selection.setBufferRange(stop.getBufferRange())
+ }
+ }
+
+ // What happens when the user clicks inside the choice, resulting in it nolonger being selected
+ disposables.add(
+ atom.keymaps.add(module.filename, { [target]: { 'shift-tab': cycle } }),
+ atom.commands.add(target, cycle, event => choices.next()),
+ cursor.onDidChangePosition(({ newBufferPosition }) => {
+ if (!stop.getBufferRange().containsPoint(newBufferPosition)) {
+ disposables.dispose()
+ }
+ }))
+ }
+ }
+
+ expand (editor, cursor, tabstops, variables) {
+ if (!(this.identifier in variables)) {
+ this.mark({ tabstops, ...this.insert(editor, cursor, this.default) })
+ } else {
+ super.expand(editor, cursor, tabstops, variables)
+ }
+ }
+
+ toString () {
+ return this.default.toString()
+ }
+}
diff --git a/expression/expression.js b/expression/expression.js
new file mode 100644
index 00000000..92367ac0
--- /dev/null
+++ b/expression/expression.js
@@ -0,0 +1,45 @@
+module.exports = class Expression {
+ constructor (identifier) {
+ this.identifier = identifier
+ }
+
+ expand (editor, cursor, tabstops, variables) {
+ // Check whether we are a tabstop or a variable
+ Number.isInteger(this.identifier)
+ // Create a tabstop marker at our position
+ ? this.mark({ tabstops, start: cursor.getBufferPosition() })
+ // Check whether we are a know variable or not
+ : this.identifier in variables
+ // Insert the variables value
+ ? this.insert(editor, cursor, variables[this.identifier])
+ // Insert 'this.identifier' and create a tabstop marker with it selected
+ : this.mark({ tabstops, ...this.insert(editor, cursor, this.identifier) })
+ }
+
+ insert (editor, cursor, value) {
+ return editor.getBuffer().insert(cursor.getBufferPosition(), value)
+ }
+
+ activate (editor, cursor, stop, mirror) {
+ if (mirror === stop) {
+ cursor.selection.setBufferRange(stop.getBufferRange())
+ const subscription = cursor.onDidChangePosition(({ newBufferPosition }) => {
+ if (!stop.getBufferRange().containsPoint(newBufferPosition)) {
+ stop.destroy()
+ subscription.dispose()
+ }
+ })
+ } else {
+ editor.decorateMarker(mirror, { type: 'highlight' })
+ stop.onDidDestroy(() => mirror.destroy())
+ }
+ }
+
+ mark ({ tabstops, start, end = start, exclusive = true, expression = this }) {
+ tabstops.markBufferRange({ start, end }, { exclusive }).setProperties({ expression })
+ }
+
+ toString () {
+ return ''
+ }
+}
diff --git a/expression/placeholder.js b/expression/placeholder.js
new file mode 100644
index 00000000..5ef36e8d
--- /dev/null
+++ b/expression/placeholder.js
@@ -0,0 +1,21 @@
+const Expression = require('./expression')
+
+module.exports = class Placeholder extends Expression {
+ constructor (identifier, snippet) {
+ super(identifier)
+
+ this.snippet = snippet
+ }
+
+ expand (editor, cursor, tabstops, variables) {
+ if (!(this.identifier in variables)) {
+ this.mark({ tabstops, ...this.snippet.expand({ editor, cursor, tabstops, variables }) })
+ } else {
+ super.expand(editor, cursor, tabstops, variables)
+ }
+ }
+
+ toString () {
+ return this.snippet.toString()
+ }
+}
diff --git a/expression/snippet.js b/expression/snippet.js
new file mode 100644
index 00000000..2dec54e9
--- /dev/null
+++ b/expression/snippet.js
@@ -0,0 +1,185 @@
+const { CompositeDisposable } = require('atom')
+const path = require('path')
+
+const Expression = require('./expression')
+
+module.exports = class Snippet extends Expression {
+ static VARIABLES = {
+ // The currently selected text or the empty string
+ TM_SELECTED_TEXT: (editor, cursor) => cursor.selection.getText(),
+ // The contents of the current line
+ TM_CURRENT_LINE: (editor, cursor) => cursor.getCurrentBufferLine(),
+ // The contents of the word under cursor or the empty string
+ TM_CURRENT_WORD: (editor, cursor) => editor.getTextInBufferRange(cursor.getCurrentWordBufferRange()),
+ // The zero-index based line number
+ TM_LINE_INDEX: (editor, cursor) => cursor.getBufferRow().toString(),
+ // The one-index based line number
+ // Does 'getScreenRow'work as intended?
+ TM_LINE_NUMBER: (editor, cursor) => (cursor.getScreenRow() + 1).toString(),
+ // The filename of the current document
+ TM_FILENAME: (editor, cursor) => editor.getTitle(),
+ // The filename of the current document without its extensions
+ TM_FILENAME_BASE: (editor, cursor, filepath = editor.getTitle()) => path.basename(filepath, path.extname(filepath)),
+ // The directory of the current document
+ TM_DIRECTORY: (editor, cursor) => path.dirname(editor.getPath()),
+ // The full file path of the current document
+ TM_FILEPATH: (editor, cursor) => editor.getPath(),
+ // The contents of the clipboard
+ CLIPBOARD: (editor, cursor) => atom.clipboard.read(),
+ // The name of the opened workspace or folder
+ WORKSPACE_NAME: (editor, cursor, [projectPath] = atom.project.relativizePath(editor.getPath())) => path.basename(projectPath),
+ // Insert the current date and time
+ // The current year
+ CURRENT_YEAR: (editor, cursor) => new Date().toLocaleString('default', { year: 'numeric' }),
+ // The current year's last two digits
+ CURRENT_YEAR_SHORT: (editor, cursor) => new Date().toLocaleString('default', { year: '2-digit' }),
+ // The month as two digits
+ CURRENT_MONTH: (editor, cursor) => new Date().toLocaleString('default', { month: '2-digit' }),
+ // The full name of the month
+ CURRENT_MONTH_NAME: (editor, cursor) => new Date().toLocaleString('default', { month: 'long' }),
+ // The short name of the month
+ CURRENT_MONTH_NAME_SHORT: (editor, cursor) => new Date().toLocaleString('default', { month: 'short' }),
+ // The day of the month
+ CURRENT_DATE: (editor, cursor) => new Date().toLocaleString('default', { day: '2-digit' }),
+ // The name of day
+ CURRENT_DAY_NAME: (editor, cursor) => new Date().toLocaleString('default', { weekday: 'long' }),
+ // The short name of the day
+ CURRENT_DAY_NAME_SHORT: (editor, cursor) => new Date().toLocaleString('default', { weekday: 'short' }),
+ // The current hour in 24-hour clock format
+ CURRENT_HOUR: (editor, cursor) => new Date().toLocaleString('default', { hour: '2-digit' }),
+ // The current minute
+ CURRENT_MINUTE: (editor, cursor) => new Date().toLocaleString('default', { minute: '2-digit' }),
+ // The current second
+ CURRENT_SECOND: (editor, cursor) => new Date().toLocaleString('default', { second: '2-digit' }),
+ // The number of seconds since the Unix epoch
+ CURRENT_SECONDS_UNIX: (editor, cursor) => Math.round(new Date() / 1000).toString(),
+ //
+ // TODO?:
+ // Insert line or block comments, honoring the current language
+ // BLOCK_COMMENT_START
+ // BLOCK_COMMENT_END
+ // LINE_COMMENT
+ //
+ // custom = custom variables
+ // TODO: I dont remember what this cache is supposed to do
+ PROXY: (editor, cursor, custom) => new Proxy({}, {
+ get: (cache, property) => property in this.VARIABLES
+ ? (cache[property] = this.VARIABLES[property](editor, cursor))
+ : property in custom
+ ? custom[property]
+ // We should never see this value used
+ : null,
+ has: (cache, property) => property in this.VARIABLES || property in custom
+ })
+ }
+
+ static getTabstops (markers) {
+ const tabstops = []
+ const unknowns = []
+
+ markers.forEach(marker => {
+ const { expression } = marker.getProperties()
+
+ Number.isInteger(expression.identifier)
+ ? Array.isArray(tabstops[expression.identifier])
+ ? tabstops[expression.identifier].push(marker)
+ : tabstops[expression.identifier] = [marker]
+ : unknowns.push([marker])
+ })
+ // Include all unknown variables at the end
+ if (unknowns.length) {
+ tabstops.push(...unknowns)
+ }
+ // Move 0th tabstop to last
+ tabstops.push(tabstops.shift())
+
+ return tabstops
+ }
+
+ constructor (body) {
+ // This snippet will work as the default ending tabstop
+ super(0)
+
+ this.body = body
+ }
+
+ // We work as the default ending tabstop, this is a special case
+ activate (editor, cursor, stop, mirror) {
+ cursor.setBufferPosition(stop.getBufferRange().end)
+ }
+
+ expand ({
+ editor = atom.workspace.getActiveTextEditor(),
+ cursor = editor.getLastCursor(),
+ tabstops = editor.addMarkerLayer(), // This is a _Display_MarkerLayer
+ variables = {}
+ } = {}) {
+ if (this.legacySyntax) {
+ atom.notifications.addWarning('Snippets: Snippet uses deprecated syntax.', {
+ description: 'Old syntactic features will be removed in a future release',
+ dismissable: true
+ })
+ }
+
+ // Expression a variable proxy to access given and built-in variables
+ variables = Snippet.VARIABLES.PROXY(editor, cursor, variables)
+
+ // Define a marker that spans the whole snippet
+ // This will also be used as the ending tabstop if there isn't an explicit one
+ // Don't make this marker exclusive, so it expands with the inserts bellow
+ this.mark({ tabstops, start: cursor.getBufferPosition(), exclusive: false })
+ const marker = tabstops.getMarkers().pop()
+ // Take care that our disposables are disposed if necessary
+ const disposables = new CompositeDisposable(
+ cursor.onDidDestroy(() => disposables.dispose()),
+ cursor.onDidChangePosition(({ newBufferPosition }) => {
+ // Exclude endpoints, so that end tabstops don't trigger mirror logic
+ if (!marker.getBufferRange().containsPoint(newBufferPosition, true)) {
+ disposables.dispose()
+ }
+ }))
+
+ // We are the outmost snippet
+ const parentSnippet = tabstops.getMarkerCount() === 1
+
+ this.body.forEach(value => value instanceof Object
+ ? value.expand(editor, cursor, tabstops, variables)
+ : this.insert(editor, cursor, value))
+
+ // Only create tabstop stuff if we have any
+ if (parentSnippet && tabstops.getMarkerCount() > 1) {
+ const target = 'atom-text-editor:not([mini])'
+ const iterate = `snippets:next-tab-stop-${tabstops.id}`
+ // The markers aren't guaranteed to be in insertion order, as they're stored in an Object
+ // Luckilly the ids used are integers and the values are fetched using 'Object.values'
+ const stops = {
+ iterator: Snippet.getTabstops(tabstops.getMarkers()).values(),
+ next () {
+ const iteration = this.iterator.next()
+ if (!iteration.done) {
+ const { value: [stop] } = iteration
+ const { expression } = stop.getProperties()
+ iteration.value.forEach(mirror => expression.activate(editor, cursor, stop, mirror))
+ return true
+ }
+ disposables.dispose()
+ }
+ }
+
+ disposables.add(
+ { dispose: () => tabstops.destroy() },
+ atom.keymaps.add(module.filename, { [target]: { tab: iterate } }),
+ atom.commands.add(target, iterate, event => stops.next() ||
+ event.abortKeyBinding()))
+
+ // Go to the first tabstop
+ stops.next()
+ }
+
+ return marker.getBufferRange()
+ }
+
+ toString () {
+ return this.body.reduce((result, value) => result + value)
+ }
+}
diff --git a/expression/transformation.js b/expression/transformation.js
new file mode 100644
index 00000000..2d76d1a9
--- /dev/null
+++ b/expression/transformation.js
@@ -0,0 +1,40 @@
+const Expression = require('./expression')
+
+module.exports = class Transformation extends Expression {
+ constructor (identifier, [regexp, format, flags]) {
+ super(identifier)
+
+ this.regexp = new RegExp(regexp, flags.join(''))
+ this.format = format
+ }
+
+ activate (editor, cursor, stop, mirror) {
+ super.activate(editor, cursor, stop, mirror)
+ mirror.onDidDestroy(() => {
+ const range = mirror.getBufferRange()
+ const buffer = editor.getBuffer()
+ buffer.setTextInRange(range, this.transform(buffer.getTextInRange(range)))
+ })
+ }
+
+ transform (string, regexp = this.regexp) {
+ let fold = sequence => sequence
+ return this.format.reduce((result, sequence) => {
+ const [group, insertion, replacement = ''] = sequence
+ sequence instanceof Function
+ ? fold = sequence
+ : sequence instanceof Object
+ ? result += fold(string.replace(regexp, group) ? insertion : replacement)
+ : result += fold(string.replace(regexp, sequence))
+ return result
+ }, '')
+ }
+
+ insert (editor, cursor, value) {
+ return super.insert(editor, cursor, this.transform(value))
+ }
+
+ toString () {
+ return this.transform(super.toString())
+ }
+}
diff --git a/keymaps/snippets-1.cson b/keymaps/snippets-1.cson
deleted file mode 100644
index ac786f45..00000000
--- a/keymaps/snippets-1.cson
+++ /dev/null
@@ -1,2 +0,0 @@
-'atom-text-editor:not([mini])':
- 'tab': 'snippets:expand'
diff --git a/keymaps/snippets-2.cson b/keymaps/snippets-2.cson
deleted file mode 100644
index 1ce10c9b..00000000
--- a/keymaps/snippets-2.cson
+++ /dev/null
@@ -1,6 +0,0 @@
-# it's critical that these bindings be loaded after those snippets-1 so they
-# are later in the cascade, hence breaking the keymap into 2 files
-
-'atom-text-editor:not([mini])':
- 'tab': 'snippets:next-tab-stop'
- 'shift-tab': 'snippets:previous-tab-stop'
diff --git a/lib/editor-store.js b/lib/editor-store.js
deleted file mode 100644
index c57cb7ad..00000000
--- a/lib/editor-store.js
+++ /dev/null
@@ -1,73 +0,0 @@
-const SnippetHistoryProvider = require('./snippet-history-provider')
-
-class EditorStore {
- constructor (editor) {
- this.editor = editor
- this.buffer = this.editor.getBuffer()
- this.observer = null
- this.checkpoint = null
- this.expansions = []
- this.existingHistoryProvider = null
- }
-
- getExpansions () {
- return this.expansions
- }
-
- setExpansions (list) {
- this.expansions = list
- }
-
- clearExpansions () {
- this.expansions = []
- }
-
- addExpansion (snippetExpansion) {
- this.expansions.push(snippetExpansion)
- }
-
- observeHistory (delegates) {
- if (this.existingHistoryProvider == null) {
- this.existingHistoryProvider = this.buffer.historyProvider
- }
-
- const newProvider = SnippetHistoryProvider(this.existingHistoryProvider, delegates)
- this.buffer.setHistoryProvider(newProvider)
- }
-
- stopObservingHistory (editor) {
- if (this.existingHistoryProvider == null) { return }
- this.buffer.setHistoryProvider(this.existingHistoryProvider)
- this.existingHistoryProvider = null
- }
-
- observe (callback) {
- if (this.observer != null) { this.observer.dispose() }
- this.observer = this.buffer.onDidChangeText(callback)
- }
-
- stopObserving () {
- if (this.observer == null) { return false }
- this.observer.dispose()
- this.observer = null
- return true
- }
-
- makeCheckpoint () {
- const existing = this.checkpoint
- if (existing) {
- this.buffer.groupChangesSinceCheckpoint(existing)
- }
- this.checkpoint = this.buffer.createCheckpoint()
- }
-}
-
-EditorStore.store = new WeakMap()
-EditorStore.findOrCreate = function (editor) {
- if (!this.store.has(editor)) {
- this.store.set(editor, new EditorStore(editor))
- }
- return this.store.get(editor)
-}
-
-module.exports = EditorStore
diff --git a/lib/helpers.js b/lib/helpers.js
deleted file mode 100644
index 0814a3df..00000000
--- a/lib/helpers.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/** @babel */
-
-import path from 'path'
-
-export function getPackageRoot() {
- const {resourcePath} = atom.getLoadSettings()
- const currentFileWasRequiredFromSnapshot = !path.isAbsolute(__dirname)
- if (currentFileWasRequiredFromSnapshot) {
- return path.join(resourcePath, 'node_modules', 'snippets')
- } else {
- return path.resolve(__dirname, '..')
- }
-}
diff --git a/lib/insertion.js b/lib/insertion.js
deleted file mode 100644
index 96065d1e..00000000
--- a/lib/insertion.js
+++ /dev/null
@@ -1,94 +0,0 @@
-const ESCAPES = {
- u: (flags) => {
- flags.lowercaseNext = false
- flags.uppercaseNext = true
- },
- l: (flags) => {
- flags.uppercaseNext = false
- flags.lowercaseNext = true
- },
- U: (flags) => {
- flags.lowercaseAll = false
- flags.uppercaseAll = true
- },
- L: (flags) => {
- flags.uppercaseAll = false
- flags.lowercaseAll = true
- },
- E: (flags) => {
- flags.uppercaseAll = false
- flags.lowercaseAll = false
- },
- r: (flags, result) => {
- result.push('\\r')
- },
- n: (flags, result) => {
- result.push('\\n')
- },
- $: (flags, result) => {
- result.push('$')
- }
-}
-
-function transformText (str, flags) {
- if (flags.uppercaseAll) {
- return str.toUpperCase()
- } else if (flags.lowercaseAll) {
- return str.toLowerCase()
- } else if (flags.uppercaseNext) {
- flags.uppercaseNext = false
- return str.replace(/^./, s => s.toUpperCase())
- } else if (flags.lowercaseNext) {
- return str.replace(/^./, s => s.toLowerCase())
- }
- return str
-}
-
-class Insertion {
- constructor ({ range, substitution }) {
- this.range = range
- this.substitution = substitution
- if (substitution) {
- if (substitution.replace === undefined) {
- substitution.replace = ''
- }
- this.replacer = this.makeReplacer(substitution.replace)
- }
- }
-
- isTransformation () {
- return !!this.substitution
- }
-
- makeReplacer (replace) {
- return function replacer (...match) {
- let flags = {
- uppercaseAll: false,
- lowercaseAll: false,
- uppercaseNext: false,
- lowercaseNext: false
- }
- replace = [...replace]
- let result = []
- replace.forEach(token => {
- if (typeof token === 'string') {
- result.push(transformText(token, flags))
- } else if (token.escape) {
- ESCAPES[token.escape](flags, result)
- } else if (token.backreference) {
- let transformed = transformText(match[token.backreference], flags)
- result.push(transformed)
- }
- })
- return result.join('')
- }
- }
-
- transform (input) {
- let { substitution } = this
- if (!substitution) { return input }
- return input.replace(substitution.find, this.replacer)
- }
-}
-
-module.exports = Insertion
diff --git a/lib/snippet-body-parser.js b/lib/snippet-body-parser.js
deleted file mode 100644
index d4293ecb..00000000
--- a/lib/snippet-body-parser.js
+++ /dev/null
@@ -1,14 +0,0 @@
-let parser
-try {
- parser = require('./snippet-body')
-} catch (error) {
- const {allowUnsafeEval} = require('loophole')
- const fs = require('fs-plus')
- const PEG = require('pegjs')
-
- const grammarSrc = fs.readFileSync(require.resolve('./snippet-body.pegjs'), 'utf8')
- parser = null
- allowUnsafeEval(() => parser = PEG.buildParser(grammarSrc))
-}
-
-module.exports = parser
diff --git a/lib/snippet-body.pegjs b/lib/snippet-body.pegjs
deleted file mode 100644
index 476c65af..00000000
--- a/lib/snippet-body.pegjs
+++ /dev/null
@@ -1,82 +0,0 @@
-{
- // Joins all consecutive strings in a collection without clobbering any
- // non-string members.
- function coalesce (parts) {
- const result = [];
- for (let i = 0; i < parts.length; i++) {
- const part = parts[i];
- const ri = result.length - 1;
- if (typeof part === 'string' && typeof result[ri] === 'string') {
- result[ri] = result[ri] + part;
- } else {
- result.push(part);
- }
- }
- return result;
- }
-
- function flatten (parts) {
- return parts.reduce(function (flat, rest) {
- return flat.concat(Array.isArray(rest) ? flatten(rest) : rest);
- }, []);
- }
-}
-bodyContent = content:(tabStop / bodyContentText)* { return content; }
-bodyContentText = text:bodyContentChar+ { return text.join(''); }
-bodyContentChar = escaped / !tabStop char:. { return char; }
-
-escaped = '\\' char:. { return char; }
-tabStop = tabStopWithTransformation / tabStopWithPlaceholder / tabStopWithoutPlaceholder / simpleTabStop
-
-simpleTabStop = '$' index:[0-9]+ {
- return { index: parseInt(index.join("")), content: [] };
-}
-tabStopWithoutPlaceholder = '${' index:[0-9]+ '}' {
- return { index: parseInt(index.join("")), content: [] };
-}
-tabStopWithPlaceholder = '${' index:[0-9]+ ':' content:placeholderContent '}' {
- return { index: parseInt(index.join("")), content: content };
-}
-tabStopWithTransformation = '${' index:[0-9]+ substitution:transformationSubstitution '}' {
- return {
- index: parseInt(index.join(""), 10),
- content: [],
- substitution: substitution
- };
-}
-
-placeholderContent = content:(tabStop / placeholderContentText / variable )* { return flatten(content); }
-placeholderContentText = text:placeholderContentChar+ { return coalesce(text); }
-placeholderContentChar = escaped / placeholderVariableReference / !tabStop !variable char:[^}] { return char; }
-
-placeholderVariableReference = '$' digit:[0-9]+ {
- return { index: parseInt(digit.join(""), 10), content: [] };
-}
-
-variable = '${' variableContent '}' {
- return ''; // we eat variables and do nothing with them for now
-}
-variableContent = content:(variable / variableContentText)* { return content; }
-variableContentText = text:variableContentChar+ { return text.join(''); }
-variableContentChar = !variable char:('\\}' / [^}]) { return char; }
-
-escapedForwardSlash = pair:'\\/' { return pair; }
-
-// A pattern and replacement for a transformed tab stop.
-transformationSubstitution = '/' find:(escapedForwardSlash / [^/])* '/' replace:formatString* '/' flags:[imy]* {
- let reFind = new RegExp(find.join(''), flags.join('') + 'g');
- return { find: reFind, replace: replace[0] };
-}
-
-formatString = content:(formatStringEscape / formatStringReference / escapedForwardSlash / [^/])+ {
- return content;
-}
-// Backreferencing a substitution. Different from a tab stop.
-formatStringReference = '$' digits:[0-9]+ {
- return { backreference: parseInt(digits.join(''), 10) };
-};
-// One of the special control flags in a format string for case folding and
-// other tasks.
-formatStringEscape = '\\' flag:[ULulErn$] {
- return { escape: flag };
-}
diff --git a/lib/snippet-expansion.js b/lib/snippet-expansion.js
deleted file mode 100644
index 54a525bc..00000000
--- a/lib/snippet-expansion.js
+++ /dev/null
@@ -1,245 +0,0 @@
-const {CompositeDisposable, Range, Point} = require('atom')
-
-module.exports = class SnippetExpansion {
- constructor(snippet, editor, cursor, snippets) {
- this.settingTabStop = false
- this.isIgnoringBufferChanges = false
- this.onUndoOrRedo = this.onUndoOrRedo.bind(this)
- this.snippet = snippet
- this.editor = editor
- this.cursor = cursor
- this.snippets = snippets
- this.subscriptions = new CompositeDisposable
- this.tabStopMarkers = []
- this.selections = [this.cursor.selection]
-
- const startPosition = this.cursor.selection.getBufferRange().start
- let {body, tabStopList} = this.snippet
- let tabStops = tabStopList.toArray()
-
- let indent = this.editor.lineTextForBufferRow(startPosition.row).match(/^\s*/)[0]
- if (this.snippet.lineCount > 1 && indent) {
- // Add proper leading indentation to the snippet
- body = body.replace(/\n/g, `\n${indent}`)
-
- tabStops = tabStops.map(tabStop => tabStop.copyWithIndent(indent))
- }
-
- this.editor.transact(() => {
- this.ignoringBufferChanges(() => {
- this.editor.transact(() => {
- const newRange = this.cursor.selection.insertText(body, {autoIndent: false})
- if (this.snippet.tabStopList.length > 0) {
- this.subscriptions.add(this.cursor.onDidChangePosition(event => this.cursorMoved(event)))
- this.subscriptions.add(this.cursor.onDidDestroy(() => this.cursorDestroyed()))
- this.placeTabStopMarkers(startPosition, tabStops)
- this.snippets.addExpansion(this.editor, this)
- this.editor.normalizeTabsInBufferRange(newRange)
- }
- })
- })
- })
- }
-
- // Set a flag on undo or redo so that we know not to re-apply transforms.
- // They're already accounted for in the history.
- onUndoOrRedo (isUndo) {
- this.isUndoingOrRedoing = true
- }
-
- cursorMoved ({oldBufferPosition, newBufferPosition, textChanged}) {
- if (this.settingTabStop || textChanged) { return }
- const itemWithCursor = this.tabStopMarkers[this.tabStopIndex].find(item => item.marker.getBufferRange().containsPoint(newBufferPosition))
-
- if (itemWithCursor && !itemWithCursor.insertion.isTransformation()) { return }
-
- this.destroy()
- }
-
- cursorDestroyed () { if (!this.settingTabStop) { this.destroy() } }
-
- textChanged (event) {
- if (this.isIgnoringBufferChanges) { return }
-
- // Don't try to alter the buffer if all we're doing is restoring a
- // snapshot from history.
- if (this.isUndoingOrRedoing) {
- this.isUndoingOrRedoing = false
- return
- }
-
- this.applyTransformations(this.tabStopIndex)
- }
-
- ignoringBufferChanges (callback) {
- const wasIgnoringBufferChanges = this.isIgnoringBufferChanges
- this.isIgnoringBufferChanges = true
- callback()
- this.isIgnoringBufferChanges = wasIgnoringBufferChanges
- }
-
- applyAllTransformations () {
- this.editor.transact(() => {
- this.tabStopMarkers.forEach((item, index) =>
- this.applyTransformations(index, true))
- })
- }
-
- applyTransformations (tabStop, initial = false) {
- const items = [...this.tabStopMarkers[tabStop]]
- if (items.length === 0) { return }
-
- const primary = items.shift()
- const primaryRange = primary.marker.getBufferRange()
- const inputText = this.editor.getTextInBufferRange(primaryRange)
-
- this.ignoringBufferChanges(() => {
- for (const item of items) {
- const {marker, insertion} = item
- var range = marker.getBufferRange()
-
- // Don't transform mirrored tab stops. They have their own cursors, so
- // mirroring happens automatically.
- if (!insertion.isTransformation()) { continue }
-
- var outputText = insertion.transform(inputText)
- this.editor.transact(() => this.editor.setTextInBufferRange(range, outputText))
- const newRange = new Range(
- range.start,
- range.start.traverse(new Point(0, outputText.length))
- )
- marker.setBufferRange(newRange)
- }
- })
- }
-
- placeTabStopMarkers (startPosition, tabStops) {
- for (const tabStop of tabStops) {
- const {insertions} = tabStop
- const markers = []
-
- if (!tabStop.isValid()) { continue }
-
- for (const insertion of insertions) {
- const {range} = insertion
- const {start, end} = range
- const marker = this.getMarkerLayer(this.editor).markBufferRange([
- startPosition.traverse(start),
- startPosition.traverse(end)
- ])
- markers.push({
- index: markers.length,
- marker,
- insertion
- })
- }
-
- this.tabStopMarkers.push(markers)
- }
-
- this.setTabStopIndex(0)
- this.applyAllTransformations()
- }
-
- goToNextTabStop () {
- const nextIndex = this.tabStopIndex + 1
- if (nextIndex < this.tabStopMarkers.length) {
- if (this.setTabStopIndex(nextIndex)) {
- return true
- } else {
- return this.goToNextTabStop()
- }
- } else {
- // The user has tabbed past the last tab stop. If the last tab stop is a
- // $0, we shouldn't move the cursor any further.
- if (this.snippet.tabStopList.hasEndStop) {
- this.destroy()
- return false
- } else {
- const succeeded = this.goToEndOfLastTabStop()
- this.destroy()
- return succeeded
- }
- }
- }
-
- goToPreviousTabStop () {
- if (this.tabStopIndex > 0) { this.setTabStopIndex(this.tabStopIndex - 1) }
- }
-
- setTabStopIndex (tabStopIndex) {
- this.tabStopIndex = tabStopIndex
- this.settingTabStop = true
- let markerSelected = false
-
- const items = this.tabStopMarkers[this.tabStopIndex]
- if (items.length === 0) { return false }
-
- const ranges = []
- this.hasTransforms = false
- for (const item of items) {
- const {marker, insertion} = item
- if (marker.isDestroyed()) { continue }
- if (!marker.isValid()) { continue }
- if (insertion.isTransformation()) {
- this.hasTransforms = true
- continue
- }
- ranges.push(marker.getBufferRange())
- }
-
- if (ranges.length > 0) {
- for (const selection of this.selections.slice(ranges.length)) { selection.destroy() }
- this.selections = this.selections.slice(0, ranges.length)
- for (let i = 0; i < ranges.length; i++) {
- const range = ranges[i]
- if (this.selections[i]) {
- this.selections[i].setBufferRange(range)
- } else {
- const newSelection = this.editor.addSelectionForBufferRange(range)
- this.subscriptions.add(newSelection.cursor.onDidChangePosition(event => this.cursorMoved(event)))
- this.subscriptions.add(newSelection.cursor.onDidDestroy(() => this.cursorDestroyed()))
- this.selections.push(newSelection)
- }
- }
- markerSelected = true
- }
-
- this.settingTabStop = false
- // If this snippet has at least one transform, we need to observe changes
- // made to the editor so that we can update the transformed tab stops.
- if (this.hasTransforms) { this.snippets.observeEditor(this.editor) }
-
- return markerSelected
- }
-
- goToEndOfLastTabStop () {
- if (this.tabStopMarkers.length === 0) { return }
- const items = this.tabStopMarkers[this.tabStopMarkers.length - 1]
- if (items.length === 0) { return }
- const {marker: lastMarker} = items[items.length - 1]
- if (lastMarker.isDestroyed()) {
- return false
- } else {
- this.editor.setCursorBufferPosition(lastMarker.getEndBufferPosition())
- return true
- }
- }
-
- destroy () {
- this.subscriptions.dispose()
- this.getMarkerLayer(this.editor).clear()
- this.tabStopMarkers = []
- this.snippets.stopObservingEditor(this.editor)
- this.snippets.clearExpansions(this.editor)
- }
-
- getMarkerLayer () {
- return this.snippets.findOrCreateMarkerLayer(this.editor)
- }
-
- restore (editor) {
- this.editor = editor
- this.snippets.addExpansion(this.editor, this)
- }
-}
diff --git a/lib/snippet-history-provider.js b/lib/snippet-history-provider.js
deleted file mode 100644
index b1b3e57c..00000000
--- a/lib/snippet-history-provider.js
+++ /dev/null
@@ -1,27 +0,0 @@
-function wrap (manager, callbacks) {
- let klass = new SnippetHistoryProvider(manager)
- return new Proxy(manager, {
- get (target, name) {
- if (name in callbacks) {
- callbacks[name]()
- }
- return name in klass ? klass[name] : target[name]
- }
- })
-}
-
-class SnippetHistoryProvider {
- constructor (manager) {
- this.manager = manager
- }
-
- undo (...args) {
- return this.manager.undo(...args)
- }
-
- redo (...args) {
- return this.manager.redo(...args)
- }
-}
-
-module.exports = wrap
diff --git a/lib/snippet.js b/lib/snippet.js
deleted file mode 100644
index fcdfed90..00000000
--- a/lib/snippet.js
+++ /dev/null
@@ -1,56 +0,0 @@
-const {Range} = require('atom')
-const TabStopList = require('./tab-stop-list')
-
-module.exports = class Snippet {
- constructor({name, prefix, bodyText, description, descriptionMoreURL, rightLabelHTML, leftLabel, leftLabelHTML, bodyTree}) {
- this.name = name
- this.prefix = prefix
- this.bodyText = bodyText
- this.description = description
- this.descriptionMoreURL = descriptionMoreURL
- this.rightLabelHTML = rightLabelHTML
- this.leftLabel = leftLabel
- this.leftLabelHTML = leftLabelHTML
- this.tabStopList = new TabStopList(this)
- this.body = this.extractTabStops(bodyTree)
- }
-
- extractTabStops (bodyTree) {
- const bodyText = []
- let row = 0
- let column = 0
-
- // recursive helper function; mutates vars above
- let extractTabStops = bodyTree => {
- for (const segment of bodyTree) {
- if (segment.index != null) {
- let {index, content, substitution} = segment
- if (index === 0) { index = Infinity; }
- const start = [row, column]
- extractTabStops(content)
- const range = new Range(start, [row, column])
- const tabStop = this.tabStopList.findOrCreate({
- index,
- snippet: this
- })
- tabStop.addInsertion({ range, substitution })
- } else if (typeof segment === 'string') {
- bodyText.push(segment)
- var segmentLines = segment.split('\n')
- column += segmentLines.shift().length
- let nextLine
- while ((nextLine = segmentLines.shift()) != null) {
- row += 1
- column = nextLine.length
- }
- }
- }
- }
-
- extractTabStops(bodyTree)
- this.lineCount = row + 1
- this.insertions = this.tabStopList.getInsertions()
-
- return bodyText.join('')
- }
-}
diff --git a/lib/snippets-available.js b/lib/snippets-available.js
deleted file mode 100644
index d244cb16..00000000
--- a/lib/snippets-available.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/** @babel */
-
-import _ from 'underscore-plus'
-import SelectListView from 'atom-select-list'
-
-export default class SnippetsAvailable {
- constructor (snippets) {
- this.panel = null
- this.snippets = snippets
- this.selectListView = new SelectListView({
- items: [],
- filterKeyForItem: (snippet) => snippet.searchText,
- elementForItem: (snippet) => {
- const li = document.createElement('li')
- li.classList.add('two-lines')
-
- const primaryLine = document.createElement('div')
- primaryLine.classList.add('primary-line')
- primaryLine.textContent = snippet.prefix
- li.appendChild(primaryLine)
-
- const secondaryLine = document.createElement('div')
- secondaryLine.classList.add('secondary-line')
- secondaryLine.textContent = snippet.name
- li.appendChild(secondaryLine)
-
- return li
- },
- didConfirmSelection: (snippet) => {
- for (const cursor of this.editor.getCursors()) {
- this.snippets.insert(snippet.bodyText, this.editor, cursor)
- }
- this.cancel()
- },
- didConfirmEmptySelection: () => {
- this.cancel()
- },
- didCancelSelection: () => {
- this.cancel()
- }
- })
- this.selectListView.element.classList.add('available-snippets')
- this.element = this.selectListView.element
- }
-
- async toggle (editor) {
- this.editor = editor
- if (this.panel != null) {
- this.cancel()
- } else {
- this.selectListView.reset()
- await this.populate()
- this.attach()
- }
- }
-
- cancel () {
- this.editor = null
-
- if (this.panel != null) {
- this.panel.destroy()
- this.panel = null
- }
-
- if (this.previouslyFocusedElement) {
- this.previouslyFocusedElement.focus()
- this.previouslyFocusedElement = null
- }
- }
-
- populate () {
- const snippets = Object.values(this.snippets.getSnippets(this.editor))
- for (let snippet of snippets) {
- snippet.searchText = _.compact([snippet.prefix, snippet.name]).join(' ')
- }
- return this.selectListView.update({items: snippets})
- }
-
- attach () {
- this.previouslyFocusedElement = document.activeElement
- this.panel = atom.workspace.addModalPanel({item: this})
- this.selectListView.focus()
- }
-}
diff --git a/lib/snippets.js b/lib/snippets.js
deleted file mode 100644
index 8e67ec0b..00000000
--- a/lib/snippets.js
+++ /dev/null
@@ -1,666 +0,0 @@
-const path = require('path')
-const {Emitter, Disposable, CompositeDisposable, File} = require('atom')
-const _ = require('underscore-plus')
-const async = require('async')
-const CSON = require('season')
-const fs = require('fs-plus')
-const ScopedPropertyStore = require('scoped-property-store')
-
-const Snippet = require('./snippet')
-const SnippetExpansion = require('./snippet-expansion')
-const EditorStore = require('./editor-store')
-const {getPackageRoot} = require('./helpers')
-
-module.exports = {
- activate () {
- this.loaded = false
- this.userSnippetsPath = null
- this.snippetIdCounter = 0
- this.snippetsByPackage = new Map
- this.parsedSnippetsById = new Map
- this.editorMarkerLayers = new WeakMap
-
- this.scopedPropertyStore = new ScopedPropertyStore
- // The above ScopedPropertyStore will store the main registry of snippets.
- // But we need a separate ScopedPropertyStore for the snippets that come
- // from disabled packages. They're isolated so that they're not considered
- // as candidates when the user expands a prefix, but we still need the data
- // around so that the snippets provided by those packages can be shown in
- // the settings view.
- this.disabledSnippetsScopedPropertyStore = new ScopedPropertyStore
-
- this.subscriptions = new CompositeDisposable
- this.subscriptions.add(atom.workspace.addOpener(uri => {
- if (uri === 'atom://.atom/snippets') {
- return atom.workspace.openTextFile(this.getUserSnippetsPath())
- }
- }))
-
- this.loadAll()
- this.watchUserSnippets(watchDisposable => {
- this.subscriptions.add(watchDisposable)
- })
-
- this.subscriptions.add(atom.config.onDidChange('core.packagesWithSnippetsDisabled', ({newValue, oldValue}) => {
- this.handleDisabledPackagesDidChange(newValue, oldValue)
- }))
-
- const snippets = this
-
- this.subscriptions.add(atom.commands.add('atom-text-editor', {
- 'snippets:expand'(event) {
- const editor = this.getModel()
- if (snippets.snippetToExpandUnderCursor(editor)) {
- snippets.clearExpansions(editor)
- snippets.expandSnippetsUnderCursors(editor)
- } else {
- event.abortKeyBinding()
- }
- },
-
- 'snippets:next-tab-stop'(event) {
- const editor = this.getModel()
- if (!snippets.goToNextTabStop(editor)) { event.abortKeyBinding() }
- },
-
- 'snippets:previous-tab-stop'(event) {
- const editor = this.getModel()
- if (!snippets.goToPreviousTabStop(editor)) { event.abortKeyBinding() }
- },
-
- 'snippets:available'(event) {
- const editor = this.getModel()
- const SnippetsAvailable = require('./snippets-available')
- if (snippets.availableSnippetsView == null) { snippets.availableSnippetsView = new SnippetsAvailable(snippets) }
- snippets.availableSnippetsView.toggle(editor)
- }
- }))
- },
-
- deactivate () {
- if (this.emitter != null) {
- this.emitter.dispose()
- }
- this.emitter = null
- this.editorSnippetExpansions = null
- atom.config.transact(() => this.subscriptions.dispose())
- },
-
- getUserSnippetsPath () {
- if (this.userSnippetsPath != null) { return this.userSnippetsPath }
-
- this.userSnippetsPath = CSON.resolve(path.join(atom.getConfigDirPath(), 'snippets'))
- if (this.userSnippetsPath == null) { this.userSnippetsPath = path.join(atom.getConfigDirPath(), 'snippets.cson') }
- return this.userSnippetsPath
- },
-
- loadAll () {
- this.loadBundledSnippets(bundledSnippets => {
- this.loadPackageSnippets(packageSnippets => {
- this.loadUserSnippets(userSnippets => {
- atom.config.transact(() => {
- for (const snippetSet of [bundledSnippets, packageSnippets, userSnippets]) {
- for (const filepath in snippetSet) {
- const snippetsBySelector = snippetSet[filepath]
- this.add(filepath, snippetsBySelector)
- }
- }
- })
- this.doneLoading()
- })
- })
- })
- },
-
- loadBundledSnippets (callback) {
- const bundledSnippetsPath = CSON.resolve(path.join(getPackageRoot(), 'lib', 'snippets'))
- this.loadSnippetsFile(bundledSnippetsPath, snippets => {
- const snippetsByPath = {}
- snippetsByPath[bundledSnippetsPath] = snippets
- callback(snippetsByPath)
- })
- },
-
- loadUserSnippets (callback) {
- const userSnippetsPath = this.getUserSnippetsPath()
- fs.stat(userSnippetsPath, (error, stat) => {
- if (stat != null && stat.isFile()) {
- this.loadSnippetsFile(userSnippetsPath, snippets => {
- const result = {}
- result[userSnippetsPath] = snippets
- callback(result)
- })
- } else {
- callback({})
- }
- })
- },
-
- watchUserSnippets (callback) {
- const userSnippetsPath = this.getUserSnippetsPath()
- fs.stat(userSnippetsPath, (error, stat) => {
- if (stat != null && stat.isFile()) {
- const userSnippetsFileDisposable = new CompositeDisposable()
- const userSnippetsFile = new File(userSnippetsPath)
- try {
- userSnippetsFileDisposable.add(userSnippetsFile.onDidChange(() => this.handleUserSnippetsDidChange()))
- userSnippetsFileDisposable.add(userSnippetsFile.onDidDelete(() => this.handleUserSnippetsDidChange()))
- userSnippetsFileDisposable.add(userSnippetsFile.onDidRename(() => this.handleUserSnippetsDidChange()))
- } catch (e) {
- const message = `\
- Unable to watch path: \`snippets.cson\`. Make sure you have permissions
- to the \`~/.atom\` directory and \`${userSnippetsPath}\`.
-
- On linux there are currently problems with watch sizes. See
- [this document][watches] for more info.
- [watches]:https://github.com/atom/atom/blob/master/docs/build-instructions/linux.md#typeerror-unable-to-watch-path\
- `
- atom.notifications.addError(message, {dismissable: true})
- }
-
- callback(userSnippetsFileDisposable)
- } else {
- callback(new Disposable())
- }
- })
- },
-
- // Called when a user's snippets file is changed, deleted, or moved so that we
- // can immediately re-process the snippets it contains.
- handleUserSnippetsDidChange () {
- const userSnippetsPath = this.getUserSnippetsPath()
- atom.config.transact(() => {
- this.clearSnippetsForPath(userSnippetsPath)
- this.loadSnippetsFile(userSnippetsPath, result => {
- this.add(userSnippetsPath, result)
- })
- })
- },
-
- // Called when the "Enable" checkbox is checked/unchecked in the Snippets
- // section of a package's settings view.
- handleDisabledPackagesDidChange (newDisabledPackages = [], oldDisabledPackages = []) {
- const packagesToAdd = []
- const packagesToRemove = []
- for (const p of oldDisabledPackages) {
- if (!newDisabledPackages.includes(p)) { packagesToAdd.push(p) }
- }
-
- for (const p of newDisabledPackages) {
- if (!oldDisabledPackages.includes(p)) { packagesToRemove.push(p) }
- }
-
- atom.config.transact(() => {
- for (const p of packagesToRemove) { this.removeSnippetsForPackage(p) }
- for (const p of packagesToAdd) { this.addSnippetsForPackage(p) }
- })
- },
-
- addSnippetsForPackage (packageName) {
- const snippetSet = this.snippetsByPackage.get(packageName)
- for (const filePath in snippetSet) {
- const snippetsBySelector = snippetSet[filePath]
- this.add(filePath, snippetsBySelector)
- }
- },
-
- removeSnippetsForPackage (packageName) {
- const snippetSet = this.snippetsByPackage.get(packageName)
- // Copy these snippets to the "quarantined" ScopedPropertyStore so that they
- // remain present in the list of unparsed snippets reported to the settings
- // view.
- this.addSnippetsInDisabledPackage(snippetSet)
- for (const filePath in snippetSet) {
- this.clearSnippetsForPath(filePath)
- }
- },
-
- loadPackageSnippets (callback) {
- const disabledPackageNames = atom.config.get('core.packagesWithSnippetsDisabled') || []
- const packages = atom.packages.getLoadedPackages().sort((pack, _) => {
- return /\/node_modules\//.test(pack.path) ? -1 : 1
- })
-
- const snippetsDirPaths = []
- for (const pack of packages) {
- snippetsDirPaths.push(path.join(pack.path, 'snippets'))
- }
-
- async.map(snippetsDirPaths, this.loadSnippetsDirectory.bind(this), (error, results) => {
- const zipped = []
- for (const key in results) {
- zipped.push({result: results[key], pack: packages[key]})
- }
-
- const enabledPackages = []
- for (const o of zipped) {
- // Skip packages that contain no snippets.
- if (Object.keys(o.result).length === 0) { continue }
- // Keep track of which snippets come from which packages so we can
- // unload them selectively later. All packages get put into this map,
- // even disabled packages, because we need to know which snippets to add
- // if those packages are enabled again.
- this.snippetsByPackage.set(o.pack.name, o.result)
- if (disabledPackageNames.includes(o.pack.name)) {
- // Since disabled packages' snippets won't get added to the main
- // ScopedPropertyStore, we'll keep track of them in a separate
- // ScopedPropertyStore so that they can still be represented in the
- // settings view.
- this.addSnippetsInDisabledPackage(o.result)
- } else {
- enabledPackages.push(o.result)
- }
- }
-
- callback(_.extend({}, ...enabledPackages))
- })
- },
-
- doneLoading () {
- this.loaded = true
- this.getEmitter().emit('did-load-snippets')
- },
-
- onDidLoadSnippets (callback) {
- this.getEmitter().on('did-load-snippets', callback)
- },
-
- getEmitter () {
- if (this.emitter == null) {
- this.emitter = new Emitter
- }
- return this.emitter
- },
-
- loadSnippetsDirectory (snippetsDirPath, callback) {
- fs.isDirectory(snippetsDirPath, isDirectory => {
- if (!isDirectory) { return callback(null, {}) }
-
- fs.readdir(snippetsDirPath, (error, entries) => {
- if (error) {
- console.warn(`Error reading snippets directory ${snippetsDirPath}`, error)
- return callback(null, {})
- }
-
- async.map(
- entries,
- (entry, done) => {
- const filePath = path.join(snippetsDirPath, entry)
- this.loadSnippetsFile(filePath, snippets => done(null, {filePath, snippets}))
- },
- (error, results) => {
- const snippetsByPath = {}
- for (const {filePath, snippets} of results) {
- snippetsByPath[filePath] = snippets
- }
- callback(null, snippetsByPath)
- })
- })
- })
- },
-
- loadSnippetsFile (filePath, callback) {
- if (!CSON.isObjectPath(filePath)) { return callback({}) }
- CSON.readFile(filePath, {allowDuplicateKeys: false}, (error, object = {}) => {
- if (error != null) {
- console.warn(`Error reading snippets file '${filePath}': ${error.stack != null ? error.stack : error}`)
- atom.notifications.addError(`Failed to load snippets from '${filePath}'`, {detail: error.message, dismissable: true})
- }
- callback(object)
- })
- },
-
- add (filePath, snippetsBySelector, isDisabled = false) {
- for (const selector in snippetsBySelector) {
- const snippetsByName = snippetsBySelector[selector]
- const unparsedSnippetsByPrefix = {}
- for (const name in snippetsByName) {
- const attributes = snippetsByName[name]
- const {prefix, body} = attributes
- attributes.name = name
- attributes.id = this.snippetIdCounter++
- if (typeof body === 'string') {
- unparsedSnippetsByPrefix[prefix] = attributes
- } else if (body == null) {
- unparsedSnippetsByPrefix[prefix] = null
- }
- }
-
- this.storeUnparsedSnippets(unparsedSnippetsByPrefix, filePath, selector, isDisabled)
- }
- },
-
- addSnippetsInDisabledPackage (bundle) {
- for (const filePath in bundle) {
- const snippetsBySelector = bundle[filePath]
- this.add(filePath, snippetsBySelector, true)
- }
- },
-
- getScopeChain (object) {
- let scopesArray = object
- if (object && object.getScopesArray) {
- scopesArray = object.getScopesArray()
- }
-
- return scopesArray
- .map(scope => scope[0] === '.' ? scope : `.${scope}`)
- .join(' ')
- },
-
- storeUnparsedSnippets (value, path, selector, isDisabled = false) {
- // The `isDisabled` flag determines which scoped property store we'll use.
- // Active snippets get put into one and inactive snippets get put into
- // another. Only the first one gets consulted when we look up a snippet
- // prefix for expansion, but both stores have their contents exported when
- // the settings view asks for all available snippets.
- const unparsedSnippets = {}
- unparsedSnippets[selector] = {"snippets": value}
- const store = isDisabled ? this.disabledSnippetsScopedPropertyStore : this.scopedPropertyStore
- store.addProperties(path, unparsedSnippets, {priority: this.priorityForSource(path)})
- },
-
- clearSnippetsForPath (path) {
- for (const scopeSelector in this.scopedPropertyStore.propertiesForSource(path)) {
- const object = this.scopedPropertyStore.propertiesForSourceAndSelector(path, scopeSelector)
- for (const prefix in object) {
- const attributes = object[prefix]
- this.parsedSnippetsById.delete(attributes.id)
- }
-
- this.scopedPropertyStore.removePropertiesForSourceAndSelector(path, scopeSelector)
- }
- },
-
- parsedSnippetsForScopes (scopeDescriptor) {
- let unparsedLegacySnippetsByPrefix
-
- const unparsedSnippetsByPrefix = this.scopedPropertyStore.getPropertyValue(
- this.getScopeChain(scopeDescriptor),
- "snippets"
- )
-
- const legacyScopeDescriptor = atom.config.getLegacyScopeDescriptorForNewScopeDescriptor
- ? atom.config.getLegacyScopeDescriptorForNewScopeDescriptor(scopeDescriptor)
- : undefined
-
- if (legacyScopeDescriptor) {
- unparsedLegacySnippetsByPrefix = this.scopedPropertyStore.getPropertyValue(
- this.getScopeChain(legacyScopeDescriptor),
- "snippets"
- )
- }
-
- const snippets = {}
-
- if (unparsedSnippetsByPrefix) {
- for (const prefix in unparsedSnippetsByPrefix) {
- const attributes = unparsedSnippetsByPrefix[prefix]
- if (typeof (attributes != null ? attributes.body : undefined) !== 'string') { continue }
- snippets[prefix] = this.getParsedSnippet(attributes)
- }
- }
-
- if (unparsedLegacySnippetsByPrefix) {
- for (const prefix in unparsedLegacySnippetsByPrefix) {
- const attributes = unparsedLegacySnippetsByPrefix[prefix]
- if (snippets[prefix]) { continue }
- if (typeof (attributes != null ? attributes.body : undefined) !== 'string') { continue }
- snippets[prefix] = this.getParsedSnippet(attributes)
- }
- }
-
- return snippets
- },
-
- getParsedSnippet (attributes) {
- let snippet = this.parsedSnippetsById.get(attributes.id)
- if (snippet == null) {
- let {id, prefix, name, body, bodyTree, description, descriptionMoreURL, rightLabelHTML, leftLabel, leftLabelHTML} = attributes
- if (bodyTree == null) { bodyTree = this.getBodyParser().parse(body) }
- snippet = new Snippet({id, name, prefix, bodyTree, description, descriptionMoreURL, rightLabelHTML, leftLabel, leftLabelHTML, bodyText: body})
- this.parsedSnippetsById.set(attributes.id, snippet)
- }
- return snippet
- },
-
- priorityForSource (source) {
- if (source === this.getUserSnippetsPath()) {
- return 1000
- } else {
- return 0
- }
- },
-
- getBodyParser () {
- if (this.bodyParser == null) {
- this.bodyParser = require('./snippet-body-parser')
- }
- return this.bodyParser
- },
-
- // Get an {Object} with these keys:
- // * `snippetPrefix`: the possible snippet prefix text preceding the cursor
- // * `wordPrefix`: the word preceding the cursor
- //
- // Returns `null` if the values aren't the same for all cursors
- getPrefixText (snippets, editor) {
- const wordRegex = this.wordRegexForSnippets(snippets)
-
- let snippetPrefix = null
- let wordPrefix = null
-
- for (const cursor of editor.getCursors()) {
- const position = cursor.getBufferPosition()
-
- const prefixStart = cursor.getBeginningOfCurrentWordBufferPosition({wordRegex})
- const cursorSnippetPrefix = editor.getTextInRange([prefixStart, position])
- if ((snippetPrefix != null) && (cursorSnippetPrefix !== snippetPrefix)) { return null }
- snippetPrefix = cursorSnippetPrefix
-
- const wordStart = cursor.getBeginningOfCurrentWordBufferPosition()
- const cursorWordPrefix = editor.getTextInRange([wordStart, position])
- if ((wordPrefix != null) && (cursorWordPrefix !== wordPrefix)) { return null }
- wordPrefix = cursorWordPrefix
- }
-
- return {snippetPrefix, wordPrefix}
- },
-
- // Get a RegExp of all the characters used in the snippet prefixes
- wordRegexForSnippets (snippets) {
- const prefixes = {}
-
- for (const prefix in snippets) {
- for (const character of prefix) { prefixes[character] = true }
- }
-
- const prefixCharacters = Object.keys(prefixes).join('')
- return new RegExp(`[${_.escapeRegExp(prefixCharacters)}]+`)
- },
-
- // Get the best match snippet for the given prefix text. This will return
- // the longest match where there is no exact match to the prefix text.
- snippetForPrefix (snippets, prefix, wordPrefix) {
- let longestPrefixMatch = null
-
- for (const snippetPrefix in snippets) {
- const snippet = snippets[snippetPrefix]
- if (prefix.endsWith(snippetPrefix) && (wordPrefix.length <= snippetPrefix.length)) {
- if ((longestPrefixMatch == null) || (snippetPrefix.length > longestPrefixMatch.prefix.length)) {
- longestPrefixMatch = snippet
- }
- }
- }
-
- return longestPrefixMatch
- },
-
- getSnippets (editor) {
- return this.parsedSnippetsForScopes(editor.getLastCursor().getScopeDescriptor())
- },
-
- snippetToExpandUnderCursor (editor) {
- if (!editor.getLastSelection().isEmpty()) { return false }
- const snippets = this.getSnippets(editor)
- if (_.isEmpty(snippets)) { return false }
-
- const prefixData = this.getPrefixText(snippets, editor)
- if (prefixData) {
- return this.snippetForPrefix(snippets, prefixData.snippetPrefix, prefixData.wordPrefix)
- }
- },
-
- expandSnippetsUnderCursors (editor) {
- const snippet = this.snippetToExpandUnderCursor(editor)
- if (!snippet) { return false }
-
- this.getStore(editor).observeHistory({
- undo: event => { this.onUndoOrRedo(editor, event, true) },
- redo: event => { this.onUndoOrRedo(editor, event, false) }
- })
-
- this.findOrCreateMarkerLayer(editor)
- editor.transact(() => {
- const cursors = editor.getCursors()
- for (const cursor of cursors) {
- const cursorPosition = cursor.getBufferPosition()
- const startPoint = cursorPosition.translate([0, -snippet.prefix.length], [0, 0])
- cursor.selection.setBufferRange([startPoint, cursorPosition])
- this.insert(snippet, editor, cursor)
- }
- })
- return true
- },
-
- goToNextTabStop (editor) {
- let nextTabStopVisited = false
- for (const expansion of this.getExpansions(editor)) {
- if (expansion && expansion.goToNextTabStop()) {
- nextTabStopVisited = true
- }
- }
- return nextTabStopVisited
- },
-
- goToPreviousTabStop (editor) {
- let previousTabStopVisited = false
- for (const expansion of this.getExpansions(editor)) {
- if (expansion && expansion.goToPreviousTabStop()) {
- previousTabStopVisited = true
- }
- }
- return previousTabStopVisited
- },
-
- getStore (editor) {
- return EditorStore.findOrCreate(editor)
- },
-
- findOrCreateMarkerLayer (editor) {
- let layer = this.editorMarkerLayers.get(editor)
- if (layer === undefined) {
- layer = editor.addMarkerLayer({maintainHistory: true})
- this.editorMarkerLayers.set(editor, layer)
- }
- return layer
- },
-
- getExpansions (editor) {
- return this.getStore(editor).getExpansions()
- },
-
- clearExpansions (editor) {
- const store = this.getStore(editor)
- store.clearExpansions()
- // There are no more active instances of this expansion, so we should undo
- // the spying we set up on this editor.
- store.stopObserving()
- store.stopObservingHistory()
- },
-
- addExpansion (editor, snippetExpansion) {
- this.getStore(editor).addExpansion(snippetExpansion)
- },
-
- textChanged (editor, event) {
- const store = this.getStore(editor)
- const activeExpansions = store.getExpansions()
-
- if ((activeExpansions.length === 0) || activeExpansions[0].isIgnoringBufferChanges) { return }
-
- this.ignoringTextChangesForEditor(editor, () =>
- editor.transact(() =>
- activeExpansions.map(expansion => expansion.textChanged(event)))
- )
-
- // Create a checkpoint here to consolidate all the changes we just made into
- // the transaction that prompted them.
- this.makeCheckpoint(editor)
- },
-
- // Perform an action inside the editor without triggering our `textChanged`
- // callback.
- ignoringTextChangesForEditor (editor, callback) {
- this.stopObservingEditor(editor)
- callback()
- this.observeEditor(editor)
- },
-
- observeEditor (editor) {
- this.getStore(editor).observe(event => this.textChanged(editor, event))
- },
-
- stopObservingEditor (editor) {
- this.getStore(editor).stopObserving()
- },
-
- makeCheckpoint (editor) {
- this.getStore(editor).makeCheckpoint()
- },
-
- insert (snippet, editor, cursor) {
- if (editor == null) { editor = atom.workspace.getActiveTextEditor() }
- if (cursor == null) { cursor = editor.getLastCursor() }
- if (typeof snippet === 'string') {
- const bodyTree = this.getBodyParser().parse(snippet)
- snippet = new Snippet({name: '__anonymous', prefix: '', bodyTree, bodyText: snippet})
- }
- return new SnippetExpansion(snippet, editor, cursor, this)
- },
-
- getUnparsedSnippets () {
- const results = []
- const iterate = sets => {
- for (const item of sets) {
- const newItem = _.deepClone(item)
- // The atom-slick library has already parsed the `selector` property, so
- // it's an AST here instead of a string. The object has a `toString`
- // method that turns it back into a string. That custom behavior won't
- // be preserved in the deep clone of the object, so we have to handle it
- // separately.
- newItem.selectorString = item.selector.toString()
- results.push(newItem)
- }
- }
-
- iterate(this.scopedPropertyStore.propertySets)
- iterate(this.disabledSnippetsScopedPropertyStore.propertySets)
- return results
- },
-
- provideSnippets () {
- return {
- bundledSnippetsLoaded: () => this.loaded,
- insertSnippet: this.insert.bind(this),
- snippetsForScopes: this.parsedSnippetsForScopes.bind(this),
- getUnparsedSnippets: this.getUnparsedSnippets.bind(this),
- getUserSnippetsPath: this.getUserSnippetsPath.bind(this)
- }
- },
-
- onUndoOrRedo (editor, isUndo) {
- const activeExpansions = this.getExpansions(editor)
- activeExpansions.forEach(expansion => expansion.onUndoOrRedo(isUndo))
- }
-}
diff --git a/lib/tab-stop-list.js b/lib/tab-stop-list.js
deleted file mode 100644
index 0d3bd010..00000000
--- a/lib/tab-stop-list.js
+++ /dev/null
@@ -1,48 +0,0 @@
-const TabStop = require('./tab-stop')
-
-class TabStopList {
- constructor (snippet) {
- this.snippet = snippet
- this.list = {}
- }
-
- get length () {
- return Object.keys(this.list).length
- }
-
- get hasEndStop () {
- return !!this.list[Infinity]
- }
-
- findOrCreate ({ index, snippet }) {
- if (!this.list[index]) {
- this.list[index] = new TabStop({ index, snippet })
- }
- return this.list[index]
- }
-
- forEachIndex (iterator) {
- let indices = Object.keys(this.list).sort((a1, a2) => a1 - a2)
- indices.forEach(iterator)
- }
-
- getInsertions () {
- let results = []
- this.forEachIndex(index => {
- results.push(...this.list[index].insertions)
- })
- return results
- }
-
- toArray () {
- let results = []
- this.forEachIndex(index => {
- let tabStop = this.list[index]
- if (!tabStop.isValid()) return
- results.push(tabStop)
- })
- return results
- }
-}
-
-module.exports = TabStopList
diff --git a/lib/tab-stop.js b/lib/tab-stop.js
deleted file mode 100644
index 61a423e4..00000000
--- a/lib/tab-stop.js
+++ /dev/null
@@ -1,61 +0,0 @@
-const {Range} = require('atom')
-const Insertion = require('./insertion')
-
-// A tab stop:
-// * belongs to a snippet
-// * has an index (one tab stop per index)
-// * has multiple Insertions
-class TabStop {
- constructor ({ snippet, index, insertions }) {
- this.insertions = insertions || []
- Object.assign(this, { snippet, index })
- }
-
- isValid () {
- let any = this.insertions.some(insertion => insertion.isTransformation())
- if (!any) return true
- let all = this.insertions.every(insertion => insertion.isTransformation())
- // If there are any transforming insertions, there must be at least one
- // non-transforming insertion to act as the primary.
- return !all
- }
-
- addInsertion ({ range, substitution }) {
- let insertion = new Insertion({ range, substitution })
- let insertions = this.insertions
- insertions.push(insertion)
- insertions = insertions.sort((i1, i2) => {
- return i1.range.start.compare(i2.range.start)
- })
- let initial = insertions.find(insertion => !insertion.isTransformation())
- if (initial) {
- insertions.splice(insertions.indexOf(initial), 1)
- insertions.unshift(initial)
- }
- this.insertions = insertions
- }
-
- copyWithIndent (indent) {
- let { snippet, index, insertions } = this
- let newInsertions = insertions.map(insertion => {
- let { range, substitution } = insertion
- let newRange = Range.fromObject(range, true)
- if (newRange.start.row) {
- newRange.start.column += indent.length
- newRange.end.column += indent.length
- }
- return new Insertion({
- range: newRange,
- substitution
- })
- })
-
- return new TabStop({
- snippet,
- index,
- insertions: newInsertions
- })
- }
-}
-
-module.exports = TabStop
diff --git a/menus/snippets.cson b/menus/snippets.cson
deleted file mode 100644
index 6557d2a5..00000000
--- a/menus/snippets.cson
+++ /dev/null
@@ -1,12 +0,0 @@
-'menu': [
- 'label': 'Packages'
- 'submenu': [
- 'label': 'Snippets'
- 'submenu': [
- { 'label': 'Expand', 'command': 'snippets:show' }
- { 'label': 'Next Stop', 'command': 'snippets:next-tab-stop' }
- { 'label': 'Previous Stop', 'command': 'snippets:previous-tab-stop' }
- { 'label': 'Available', 'command': 'snippets:available' }
- ]
- ]
-]
diff --git a/package-lock.json b/package-lock.json
index c0850503..cd9448e2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,25 +1,188 @@
{
"name": "snippets",
- "version": "1.6.0",
+ "version": "2.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+ "@babel/code-frame": {
+ "version": "7.12.13",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
+ "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.12.13"
+ }
+ },
+ "@babel/eslint-parser": {
+ "version": "7.13.14",
+ "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.13.14.tgz",
+ "integrity": "sha512-I0HweR36D73Ibn/FfrRDMKlMqJHFwidIUgYdMpH+aXYuQC+waq59YaJ6t9e9N36axJ82v1jR041wwqDrDXEwRA==",
+ "dev": true,
+ "requires": {
+ "eslint-scope": "^5.1.0",
+ "eslint-visitor-keys": "^1.3.0",
+ "semver": "^6.3.0"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.12.11",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
+ "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.13.10",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz",
+ "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.12.11",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@eslint/eslintrc": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz",
+ "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.4",
+ "debug": "^4.1.1",
+ "espree": "^7.3.0",
+ "globals": "^12.1.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^3.13.1",
+ "lodash": "^4.17.19",
+ "minimatch": "^3.0.4",
+ "strip-json-comments": "^3.1.1"
+ },
+ "dependencies": {
+ "globals": {
+ "version": "12.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
+ "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.8.1"
+ }
+ }
+ }
+ },
+ "@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
+ "dev": true
+ },
+ "acorn": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
+ "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
+ "dev": true
+ },
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
"ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "array-includes": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz",
+ "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.2",
+ "get-intrinsic": "^1.1.1",
+ "is-string": "^1.0.5"
+ }
+ },
+ "array.prototype.flat": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz",
+ "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.1"
+ }
+ },
+ "array.prototype.flatmap": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz",
+ "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.1",
+ "function-bind": "^1.1.1"
+ }
+ },
+ "astral-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+ "dev": true
},
"async": {
- "version": "0.2.10",
- "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
- "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E="
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
},
"atom-select-list": {
- "version": "0.7.2",
- "resolved": "https://registry.npmjs.org/atom-select-list/-/atom-select-list-0.7.2.tgz",
- "integrity": "sha512-a707OB1DhLGjzqtFrtMQKH7BBxFuCh8UBoUWxgFOrLrSwVh3g+/TlVPVDOz12+U0mDu3mIrnYLqQyhywQOTxhw==",
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/atom-select-list/-/atom-select-list-0.8.0.tgz",
+ "integrity": "sha512-LZBLl2Mn0ov/CfUV+INrfENQVVdfdXvdB4MGvmvM28Rsm/ViVAuVMjNotvZKVCo5Jm53s/Ixd8K1deQV2WHcxA==",
"requires": {
- "etch": "^0.12.6",
+ "etch": "^0.14.0",
"fuzzaldrin": "^2.1.0"
}
},
@@ -29,24 +192,51 @@
"integrity": "sha1-/w2+Fb4sTtomi50w124lF+C308o="
},
"balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"brace-expansion": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
- "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
+ "call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
"camelcase": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
"integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
},
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
"cliui": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
@@ -55,6 +245,39 @@
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"wrap-ansi": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ }
}
},
"code-point-at": {
@@ -67,43 +290,43 @@
"resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz",
"integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw=="
},
- "coffeelint": {
- "version": "1.16.0",
- "resolved": "https://registry.npmjs.org/coffeelint/-/coffeelint-1.16.0.tgz",
- "integrity": "sha1-g9jtHa/eOmd95E57ihi+YHdh5tg=",
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
- "coffee-script": "~1.11.0",
- "glob": "^7.0.6",
- "ignore": "^3.0.9",
- "optimist": "^0.6.1",
- "resolve": "^0.6.3",
- "strip-json-comments": "^1.0.2"
- },
- "dependencies": {
- "coffee-script": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.11.1.tgz",
- "integrity": "sha1-vxxHrWREOg2V0S3ysUfMCk2q1uk=",
- "dev": true
- },
- "optimist": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
- "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
- "dev": true,
- "requires": {
- "minimist": "~0.0.1",
- "wordwrap": "~0.0.2"
- }
- }
+ "color-name": "1.1.3"
}
},
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
+ "contains-path": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
+ "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
+ "dev": true
+ },
+ "cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ }
+ },
"cson-parser": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-1.3.5.tgz",
@@ -120,11 +343,44 @@
"es5-ext": "~0.10.2"
}
},
+ "debug": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
"emissary": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/emissary/-/emissary-1.3.3.tgz",
@@ -136,40 +392,101 @@
"underscore-plus": "1.x"
}
},
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "enquirer": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+ "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^4.1.1"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es-abstract": {
+ "version": "1.18.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz",
+ "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.2",
+ "is-callable": "^1.2.3",
+ "is-negative-zero": "^2.0.1",
+ "is-regex": "^1.1.2",
+ "is-string": "^1.0.5",
+ "object-inspect": "^1.9.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.2",
+ "string.prototype.trimend": "^1.0.4",
+ "string.prototype.trimstart": "^1.0.4",
+ "unbox-primitive": "^1.0.0"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
"es5-ext": {
- "version": "0.10.30",
- "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.30.tgz",
- "integrity": "sha1-cUGhaDZpfbq/qq7uQUlc4p9SyTk=",
+ "version": "0.10.53",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
+ "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
"requires": {
- "es6-iterator": "2",
- "es6-symbol": "~3.1"
+ "es6-iterator": "~2.0.3",
+ "es6-symbol": "~3.1.3",
+ "next-tick": "~1.0.0"
},
"dependencies": {
"d": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
- "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+ "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
"requires": {
- "es5-ext": "^0.10.9"
+ "es5-ext": "^0.10.50",
+ "type": "^1.0.1"
}
},
"es6-iterator": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz",
- "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
"requires": {
"d": "1",
- "es5-ext": "^0.10.14",
- "es6-symbol": "^3.1"
+ "es5-ext": "^0.10.35",
+ "es6-symbol": "^3.1.1"
}
},
"es6-symbol": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
- "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
"requires": {
- "d": "1",
- "es5-ext": "~0.10.14"
+ "d": "^1.0.1",
+ "ext": "^1.1.2"
}
}
}
@@ -204,100 +521,819 @@
"es6-symbol": "~2.0.1"
}
},
- "etch": {
- "version": "0.12.8",
- "resolved": "https://registry.npmjs.org/etch/-/etch-0.12.8.tgz",
- "integrity": "sha512-dFLRe4wLroVtwzyy1vGlE3BSDZHiL0kZME5XgNGzZIULcYTvVno8vbiIleAesoKJmwWaxDTzG+4eppg2zk14JQ=="
- },
- "event-kit": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-1.5.0.tgz",
- "integrity": "sha1-Ek72qtgyjcsmtxxHWQtbjmPrxIc=",
- "requires": {
- "grim": "^1.2.1"
- }
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
},
- "fs-plus": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-3.0.1.tgz",
- "integrity": "sha1-VMFpxA4ohKZtNSeA0Y3TH5HToQ0=",
+ "eslint": {
+ "version": "7.13.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.13.0.tgz",
+ "integrity": "sha512-uCORMuOO8tUzJmsdRtrvcGq5qposf7Rw0LwkTJkoDbOycVQtQjmnhZSuLQnozLE4TmAzlMVV45eCHmQ1OpDKUQ==",
+ "dev": true,
"requires": {
- "async": "^1.5.2",
- "mkdirp": "^0.5.1",
- "rimraf": "^2.5.2",
- "underscore-plus": "1.x"
+ "@babel/code-frame": "^7.0.0",
+ "@eslint/eslintrc": "^0.2.1",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "enquirer": "^2.3.5",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^2.1.0",
+ "eslint-visitor-keys": "^2.0.0",
+ "espree": "^7.3.0",
+ "esquery": "^1.2.0",
+ "esutils": "^2.0.2",
+ "file-entry-cache": "^5.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.0.0",
+ "globals": "^12.1.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash": "^4.17.19",
+ "minimatch": "^3.0.4",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "progress": "^2.0.0",
+ "regexpp": "^3.1.0",
+ "semver": "^7.2.1",
+ "strip-ansi": "^6.0.0",
+ "strip-json-comments": "^3.1.0",
+ "table": "^5.2.3",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
},
"dependencies": {
- "async": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
- "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "eslint-visitor-keys": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz",
+ "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==",
+ "dev": true
+ },
+ "globals": {
+ "version": "12.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
+ "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.8.1"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "semver": {
+ "version": "7.3.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+ "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
}
}
},
- "fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ "eslint-config-standard": {
+ "version": "16.0.2",
+ "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.2.tgz",
+ "integrity": "sha512-fx3f1rJDsl9bY7qzyX8SAtP8GBSk6MfXFaTfaGgk12aAYW4gJSyRm7dM790L6cbXv63fvjY4XeSzXnb4WM+SKw==",
+ "dev": true
},
- "fuzzaldrin": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz",
- "integrity": "sha1-kCBMPi/appQbso0WZF1BgGOpDps="
+ "eslint-config-standard-jsx": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-10.0.0.tgz",
+ "integrity": "sha512-hLeA2f5e06W1xyr/93/QJulN/rLbUVUmqTlexv9PRKHFwEC9ffJcH2LvJhMoEqYQBEYafedgGZXH2W8NUpt5lA==",
+ "dev": true
},
- "glob": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
- "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
+ "eslint-import-resolver-node": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz",
+ "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==",
+ "dev": true,
"requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
+ "debug": "^2.6.9",
+ "resolve": "^1.13.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
}
},
- "grim": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/grim/-/grim-1.5.0.tgz",
- "integrity": "sha1-sysI71Z88YUvgXWe2caLDXE5ajI=",
+ "eslint-module-utils": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz",
+ "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==",
+ "dev": true,
"requires": {
- "emissary": "^1.2.0"
+ "debug": "^2.6.9",
+ "pkg-dir": "^2.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
}
},
- "ignore": {
- "version": "3.3.5",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.5.tgz",
- "integrity": "sha1-xOcVRV9gc6jX5drnLS/J1xZj26Y=",
- "dev": true
- },
- "inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "eslint-plugin-es": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz",
+ "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==",
+ "dev": true,
"requires": {
- "once": "^1.3.0",
- "wrappy": "1"
+ "eslint-utils": "^2.0.0",
+ "regexpp": "^3.0.0"
}
},
- "inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
- },
- "invert-kv": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
- "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "eslint-plugin-import": {
+ "version": "2.22.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz",
+ "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==",
+ "dev": true,
"requires": {
- "number-is-nan": "^1.0.0"
- }
+ "array-includes": "^3.1.1",
+ "array.prototype.flat": "^1.2.3",
+ "contains-path": "^0.1.0",
+ "debug": "^2.6.9",
+ "doctrine": "1.5.0",
+ "eslint-import-resolver-node": "^0.3.4",
+ "eslint-module-utils": "^2.6.0",
+ "has": "^1.0.3",
+ "minimatch": "^3.0.4",
+ "object.values": "^1.1.1",
+ "read-pkg-up": "^2.0.0",
+ "resolve": "^1.17.0",
+ "tsconfig-paths": "^3.9.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "doctrine": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
+ "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2",
+ "isarray": "^1.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "eslint-plugin-node": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
+ "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==",
+ "dev": true,
+ "requires": {
+ "eslint-plugin-es": "^3.0.0",
+ "eslint-utils": "^2.0.0",
+ "ignore": "^5.1.1",
+ "minimatch": "^3.0.4",
+ "resolve": "^1.10.1",
+ "semver": "^6.1.0"
+ },
+ "dependencies": {
+ "ignore": {
+ "version": "5.1.8",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
+ "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-plugin-promise": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz",
+ "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==",
+ "dev": true
+ },
+ "eslint-plugin-react": {
+ "version": "7.21.5",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.21.5.tgz",
+ "integrity": "sha512-8MaEggC2et0wSF6bUeywF7qQ46ER81irOdWS4QWxnnlAEsnzeBevk1sWh7fhpCghPpXb+8Ks7hvaft6L/xsR6g==",
+ "dev": true,
+ "requires": {
+ "array-includes": "^3.1.1",
+ "array.prototype.flatmap": "^1.2.3",
+ "doctrine": "^2.1.0",
+ "has": "^1.0.3",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "object.entries": "^1.1.2",
+ "object.fromentries": "^2.0.2",
+ "object.values": "^1.1.1",
+ "prop-types": "^15.7.2",
+ "resolve": "^1.18.1",
+ "string.prototype.matchall": "^4.0.2"
+ },
+ "dependencies": {
+ "doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ }
+ }
+ },
+ "eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ },
+ "espree": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
+ "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.4.0",
+ "acorn-jsx": "^5.3.1",
+ "eslint-visitor-keys": "^1.3.0"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "esquery": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+ "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true
+ }
+ }
+ },
+ "esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.2.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true
+ }
+ }
+ },
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "etch": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/etch/-/etch-0.14.1.tgz",
+ "integrity": "sha512-+IwqSDBhaQFMUHJu4L/ir0dhDoW5IIihg4Z9lzsIxxne8V0PlSg0gnk2STaKWjGJQnDR4cxpA+a/dORX9kycTA=="
+ },
+ "event-kit": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-1.5.0.tgz",
+ "integrity": "sha1-Ek72qtgyjcsmtxxHWQtbjmPrxIc=",
+ "requires": {
+ "grim": "^1.2.1"
+ }
+ },
+ "ext": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+ "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+ "requires": {
+ "type": "^2.0.0"
+ },
+ "dependencies": {
+ "type": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz",
+ "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw=="
+ }
+ }
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "file-entry-cache": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+ "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^2.0.1"
+ }
+ },
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dev": true,
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ },
+ "flat-cache": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+ "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+ "dev": true,
+ "requires": {
+ "flatted": "^2.0.0",
+ "rimraf": "2.6.3",
+ "write": "1.0.3"
+ }
+ },
+ "flatted": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+ "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+ "dev": true
+ },
+ "fs-plus": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-3.1.1.tgz",
+ "integrity": "sha512-Se2PJdOWXqos1qVTkvqqjb0CSnfBnwwD+pq+z4ksT+e97mEShod/hrNg0TRCCsXPbJzcIq+NuzQhigunMWMJUA==",
+ "requires": {
+ "async": "^1.5.2",
+ "mkdirp": "^0.5.1",
+ "rimraf": "^2.5.2",
+ "underscore-plus": "1.x"
+ },
+ "dependencies": {
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ }
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
+ "fuzzaldrin": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz",
+ "integrity": "sha1-kCBMPi/appQbso0WZF1BgGOpDps="
+ },
+ "get-intrinsic": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+ "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "get-stdin": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz",
+ "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.6",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
+ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
+ "dev": true
+ },
+ "grim": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/grim/-/grim-1.5.0.tgz",
+ "integrity": "sha1-sysI71Z88YUvgXWe2caLDXE5ajI=",
+ "requires": {
+ "emissary": "^1.2.0"
+ }
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-bigints": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
+ "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "has-symbols": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+ "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "2.8.9",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+ "dev": true
+ },
+ "ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "internal-slot": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
+ "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==",
+ "dev": true,
+ "requires": {
+ "get-intrinsic": "^1.1.0",
+ "has": "^1.0.3",
+ "side-channel": "^1.0.4"
+ }
+ },
+ "invert-kv": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
+ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-bigint": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz",
+ "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==",
+ "dev": true
+ },
+ "is-boolean-object": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz",
+ "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0"
+ }
+ },
+ "is-callable": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
+ "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==",
+ "dev": true
+ },
+ "is-core-module": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
+ "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "is-date-object": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+ "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-negative-zero": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
+ "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==",
+ "dev": true
+ },
+ "is-number-object": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz",
+ "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz",
+ "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "is-string": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
+ "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
+ "dev": true
+ },
+ "is-symbol": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+ "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "jsx-ast-utils": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz",
+ "integrity": "sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==",
+ "dev": true,
+ "requires": {
+ "array-includes": "^3.1.2",
+ "object.assign": "^4.1.2"
+ }
},
"key-path-helpers": {
"version": "0.1.0",
@@ -312,72 +1348,439 @@
"invert-kv": "^1.0.0"
}
},
- "loophole": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/loophole/-/loophole-1.1.0.tgz",
- "integrity": "sha1-N5Sf6kU7YlasxyXDIM4MWn9wor0="
+ "levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ }
+ },
+ "load-json-file": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+ "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dev": true,
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+ },
+ "mixto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/mixto/-/mixto-1.0.0.tgz",
+ "integrity": "sha1-wyDvYbUvKJj1IuF9i7xtUG2EJbY="
+ },
+ "mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "mock-fs": {
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.13.0.tgz",
+ "integrity": "sha512-DD0vOdofJdoaRNtnWcrXe6RQbpHkPPmtqGq14uRX0F8ZKJ5nv89CVTYl/BZdppDxBDaV0hl75htg3abpEWlPZA==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "next-tick": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
+ },
+ "normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
+ "object-inspect": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz",
+ "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==",
+ "dev": true
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true
+ },
+ "object.assign": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ }
+ },
+ "object.entries": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz",
+ "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.1",
+ "has": "^1.0.3"
+ }
+ },
+ "object.fromentries": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz",
+ "integrity": "sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.2",
+ "has": "^1.0.3"
+ }
+ },
+ "object.values": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz",
+ "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.2",
+ "has": "^1.0.3"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "optionator": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+ "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "dev": true,
+ "requires": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ }
+ },
+ "os-locale": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
+ "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
+ "requires": {
+ "lcid": "^1.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dev": true,
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+ "dev": true
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+ },
+ "path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true
},
- "mixto": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/mixto/-/mixto-1.0.0.tgz",
- "integrity": "sha1-wyDvYbUvKJj1IuF9i7xtUG2EJbY="
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
},
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "path-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "dev": true,
"requires": {
- "minimist": "0.0.8"
+ "pify": "^2.0.0"
}
},
- "number-is-nan": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+ "pegjs": {
+ "version": "0.11.0-master.b7b87ea",
+ "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.11.0-master.b7b87ea.tgz",
+ "integrity": "sha512-fwjzNiYHRUEUe/86Aaslb/ocbbsAupOcsJz+dlPYtgp3feCDRQOLChHO924XGh7fzSJBTdFCQTzmSOQaWjCTew==",
+ "dev": true
},
- "once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "pkg-conf": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz",
+ "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==",
+ "dev": true,
"requires": {
- "wrappy": "1"
+ "find-up": "^3.0.0",
+ "load-json-file": "^5.2.0"
+ },
+ "dependencies": {
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "load-json-file": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz",
+ "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.15",
+ "parse-json": "^4.0.0",
+ "pify": "^4.0.1",
+ "strip-bom": "^3.0.0",
+ "type-fest": "^0.3.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ }
+ },
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true
+ },
+ "type-fest": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz",
+ "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==",
+ "dev": true
+ }
}
},
- "os-locale": {
- "version": "1.4.0",
- "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
- "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
+ "pkg-dir": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+ "dev": true,
"requires": {
- "lcid": "^1.0.0"
+ "find-up": "^2.1.0"
}
},
- "os-tmpdir": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
- "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+ "prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true
},
- "path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
},
- "pegjs": {
- "version": "0.8.0",
- "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.8.0.tgz",
- "integrity": "sha1-l28GfaE+XFsVAcAXklZoolOBFWE="
+ "prop-types": {
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
+ }
},
"property-accessors": {
"version": "1.1.3",
@@ -388,18 +1791,78 @@
"mixto": "1.x"
}
},
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^2.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^2.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.0.0",
+ "read-pkg": "^2.0.0"
+ }
+ },
+ "regexp.prototype.flags": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz",
+ "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3"
+ }
+ },
+ "regexpp": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
+ "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
+ "dev": true
+ },
"resolve": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz",
- "integrity": "sha1-3ZV5gufnNt699TtYpN2RdUV13UY=",
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.2.0",
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
},
"rimraf": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
- "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=",
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "dev": true,
"requires": {
- "glob": "^7.0.5"
+ "glob": "^7.1.3"
}
},
"scoped-property-store": {
@@ -424,57 +1887,339 @@
"yargs": "^3.23.0"
}
},
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
+ "shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true
+ },
+ "side-channel": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
+ }
+ },
+ "slice-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+ "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "astral-regex": "^1.0.0",
+ "is-fullwidth-code-point": "^2.0.0"
+ }
+ },
+ "spdx-correct": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz",
+ "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==",
+ "dev": true
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "standard": {
+ "version": "16.0.3",
+ "resolved": "https://registry.npmjs.org/standard/-/standard-16.0.3.tgz",
+ "integrity": "sha512-70F7NH0hSkNXosXRltjSv6KpTAOkUkSfyu3ynyM5dtRUiLtR+yX9EGZ7RKwuGUqCJiX/cnkceVM6HTZ4JpaqDg==",
+ "dev": true,
+ "requires": {
+ "eslint": "~7.13.0",
+ "eslint-config-standard": "16.0.2",
+ "eslint-config-standard-jsx": "10.0.0",
+ "eslint-plugin-import": "~2.22.1",
+ "eslint-plugin-node": "~11.1.0",
+ "eslint-plugin-promise": "~4.2.1",
+ "eslint-plugin-react": "~7.21.5",
+ "standard-engine": "^14.0.1"
+ }
+ },
+ "standard-engine": {
+ "version": "14.0.1",
+ "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-14.0.1.tgz",
+ "integrity": "sha512-7FEzDwmHDOGva7r9ifOzD3BGdTbA7ujJ50afLVdW/tK14zQEptJjbFuUfn50irqdHDcTbNh0DTIoMPynMCXb0Q==",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^8.0.0",
+ "minimist": "^1.2.5",
+ "pkg-conf": "^3.1.0",
+ "xdg-basedir": "^4.0.0"
+ }
+ },
"string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "string.prototype.matchall": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.4.tgz",
+ "integrity": "sha512-pknFIWVachNcyqRfaQSeu/FUfpvJTe4uskUSZ9Wc1RijsPuzbZ8TyYT8WCNnntCjUEqQ3vUHMAfVj2+wLAisPQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.2",
+ "has-symbols": "^1.0.1",
+ "internal-slot": "^1.0.3",
+ "regexp.prototype.flags": "^1.3.1",
+ "side-channel": "^1.0.4"
+ }
+ },
+ "string.prototype.trimend": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+ "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3"
+ }
+ },
+ "string.prototype.trimstart": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+ "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
+ "dev": true,
"requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3"
}
},
"strip-ansi": {
- "version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
"requires": {
- "ansi-regex": "^2.0.0"
+ "ansi-regex": "^5.0.0"
}
},
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true
+ },
"strip-json-comments": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
- "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "table": {
+ "version": "5.4.6",
+ "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
+ "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.10.2",
+ "lodash": "^4.17.14",
+ "slice-ansi": "^2.1.0",
+ "string-width": "^3.0.0"
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
"dev": true
},
- "temp": {
- "version": "0.8.3",
- "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz",
- "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=",
+ "tsconfig-paths": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
+ "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==",
+ "dev": true,
"requires": {
- "os-tmpdir": "^1.0.0",
- "rimraf": "~2.2.6"
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.1",
+ "minimist": "^1.2.0",
+ "strip-bom": "^3.0.0"
},
"dependencies": {
- "rimraf": {
- "version": "2.2.8",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
- "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI="
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
}
}
},
+ "type": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
+ },
+ "type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ }
+ },
+ "type-fest": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "dev": true
+ },
+ "unbox-primitive": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
+ "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has-bigints": "^1.0.1",
+ "has-symbols": "^1.0.2",
+ "which-boxed-primitive": "^1.0.2"
+ }
+ },
"underscore": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
- "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz",
+ "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g=="
},
"underscore-plus": {
- "version": "1.6.6",
- "resolved": "https://registry.npmjs.org/underscore-plus/-/underscore-plus-1.6.6.tgz",
- "integrity": "sha1-ZezeG9xEGjXYnmUP1w3PE65Dmn0=",
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/underscore-plus/-/underscore-plus-1.7.0.tgz",
+ "integrity": "sha512-A3BEzkeicFLnr+U/Q3EyWwJAQPbA19mtZZ4h+lLq3ttm9kn8WC4R3YpuJZEXmWdLjYP47Zc8aLZm9kwdv+zzvA==",
+ "requires": {
+ "underscore": "^1.9.1"
+ }
+ },
+ "uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "v8-compile-cache": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
"requires": {
- "underscore": "~1.6.0"
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-boxed-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "dev": true,
+ "requires": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
}
},
"window-size": {
@@ -482,19 +2227,52 @@
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz",
"integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY="
},
- "wordwrap": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
- "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
+ "word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
"dev": true
},
"wrap-ansi": {
"version": "2.1.0",
- "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"requires": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ }
}
},
"wrappy": {
@@ -502,14 +2280,35 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
+ "write": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
+ "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^0.5.1"
+ }
+ },
+ "xdg-basedir": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
+ "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
+ "dev": true
+ },
"y18n": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
- "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz",
+ "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ=="
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
},
"yargs": {
"version": "3.32.0",
- "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz",
"integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=",
"requires": {
"camelcase": "^2.0.1",
@@ -519,6 +2318,39 @@
"string-width": "^1.0.1",
"window-size": "^0.1.4",
"y18n": "^3.2.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ }
}
}
}
diff --git a/package.json b/package.json
index 7ced0272..1b1cbd80 100644
--- a/package.json
+++ b/package.json
@@ -1,33 +1,51 @@
{
"name": "snippets",
- "version": "1.6.0",
- "main": "./lib/snippets",
+ "version": "2.0.0",
"description": "Expand snippets matching the current prefix with `tab`.",
"repository": "https://github.com/atom/snippets",
"license": "MIT",
- "engines": {
- "atom": "*"
+ "main": "./snippets.js",
+ "scripts": {
+ "generate": "pegjs -c ./parser/config.js",
+ "lint": "standard",
+ "lint:fix": "npm run lint -- --fix",
+ "test": "atom --test specs"
},
"dependencies": {
- "async": "~0.2.6",
- "atom-select-list": "^0.7.0",
- "fs-plus": "^3.0.0",
- "loophole": "^1",
- "pegjs": "~0.8.0",
+ "atom-select-list": "^0.8.0",
"scoped-property-store": "^0.17.0",
- "season": "^6.0.2",
- "temp": "~0.8.0",
- "underscore-plus": "^1.0.0"
+ "season": "^6.0.2"
+ },
+ "devDependencies": {
+ "@babel/eslint-parser": "^7.13.14",
+ "mock-fs": "^4.13.0",
+ "pegjs": "0.11.0-master.b7b87ea",
+ "standard": "^16.0.3"
+ },
+ "engines": {
+ "atom": ">=1.56.0",
+ "node": ">=12.14.1"
},
"providedServices": {
"snippets": {
"description": "Snippets are text shortcuts that can be expanded to their definition.",
"versions": {
- "0.1.0": "provideSnippets"
+ "1.0.0": "snippets"
}
}
},
- "devDependencies": {
- "coffeelint": "^1.9.7"
+ "standard": {
+ "parser": "@babel/eslint-parser",
+ "env": {
+ "jasmine": true,
+ "node": true
+ },
+ "ignore": [
+ "spec/fixtures/"
+ ],
+ "globals": [
+ "waitsForPromise",
+ "atom"
+ ]
}
}
diff --git a/parser/config.js b/parser/config.js
new file mode 100644
index 00000000..8b62d5f5
--- /dev/null
+++ b/parser/config.js
@@ -0,0 +1,11 @@
+module.exports = {
+ input: './parser/snippet-body-parser.pegjs',
+ output: './parser/snippet-body-parser.js',
+ dependencies: {
+ Expression: '../expression/expression',
+ Snippet: '../expression/snippet',
+ Choice: '../expression/choice',
+ Placeholder: '../expression/placeholder',
+ Transformation: '../expression/transformation'
+ }
+}
diff --git a/parser/snippet-body-parser.js b/parser/snippet-body-parser.js
new file mode 100644
index 00000000..ca40945c
--- /dev/null
+++ b/parser/snippet-body-parser.js
@@ -0,0 +1,1584 @@
+// Generated by PEG.js v0.11.0-master.b7b87ea, https://pegjs.org/
+
+"use strict";
+
+var Expression = require("../expression/expression");
+var Snippet = require("../expression/snippet");
+var Choice = require("../expression/choice");
+var Placeholder = require("../expression/placeholder");
+var Transformation = require("../expression/transformation");
+
+function peg$subclass(child, parent) {
+ function C() { this.constructor = child; }
+ C.prototype = parent.prototype;
+ child.prototype = new C();
+}
+
+function peg$SyntaxError(message, expected, found, location) {
+ this.message = message;
+ this.expected = expected;
+ this.found = found;
+ this.location = location;
+ this.name = "SyntaxError";
+
+ // istanbul ignore next
+ if (typeof Error.captureStackTrace === "function") {
+ Error.captureStackTrace(this, peg$SyntaxError);
+ }
+}
+
+peg$subclass(peg$SyntaxError, Error);
+
+peg$SyntaxError.buildMessage = function(expected, found, location) {
+ var DESCRIBE_EXPECTATION_FNS = {
+ literal: function(expectation) {
+ return "\"" + literalEscape(expectation.text) + "\"";
+ },
+
+ class: function(expectation) {
+ var escapedParts = expectation.parts.map(function(part) {
+ return Array.isArray(part)
+ ? classEscape(part[0]) + "-" + classEscape(part[1])
+ : classEscape(part);
+ });
+
+ return "[" + (expectation.inverted ? "^" : "") + escapedParts + "]";
+ },
+
+ any: function() {
+ return "any character";
+ },
+
+ end: function() {
+ return "end of input";
+ },
+
+ other: function(expectation) {
+ return expectation.description;
+ },
+
+ not: function(expectation) {
+ return "not " + describeExpectation(expectation.expected);
+ }
+ };
+
+ function hex(ch) {
+ return ch.charCodeAt(0).toString(16).toUpperCase();
+ }
+
+ function literalEscape(s) {
+ return s
+ .replace(/\\/g, "\\\\")
+ .replace(/"/g, "\\\"")
+ .replace(/\0/g, "\\0")
+ .replace(/\t/g, "\\t")
+ .replace(/\n/g, "\\n")
+ .replace(/\r/g, "\\r")
+ .replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); })
+ .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); });
+ }
+
+ function classEscape(s) {
+ return s
+ .replace(/\\/g, "\\\\")
+ .replace(/\]/g, "\\]")
+ .replace(/\^/g, "\\^")
+ .replace(/-/g, "\\-")
+ .replace(/\0/g, "\\0")
+ .replace(/\t/g, "\\t")
+ .replace(/\n/g, "\\n")
+ .replace(/\r/g, "\\r")
+ .replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); })
+ .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); });
+ }
+
+ function describeExpectation(expectation) {
+ return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);
+ }
+
+ function describeExpected(expected) {
+ var descriptions = expected.map(describeExpectation);
+ var i, j;
+
+ descriptions.sort();
+
+ if (descriptions.length > 0) {
+ for (i = 1, j = 1; i < descriptions.length; i++) {
+ if (descriptions[i - 1] !== descriptions[i]) {
+ descriptions[j] = descriptions[i];
+ j++;
+ }
+ }
+ descriptions.length = j;
+ }
+
+ switch (descriptions.length) {
+ case 1:
+ return descriptions[0];
+
+ case 2:
+ return descriptions[0] + " or " + descriptions[1];
+
+ default:
+ return descriptions.slice(0, -1).join(", ")
+ + ", or "
+ + descriptions[descriptions.length - 1];
+ }
+ }
+
+ function describeFound(found) {
+ return found ? "\"" + literalEscape(found) + "\"" : "end of input";
+ }
+
+ return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";
+};
+
+function peg$parse(input, options) {
+ options = options !== undefined ? options : {};
+
+ var peg$FAILED = {};
+
+ var peg$startRuleFunctions = { Snippet: peg$parseSnippet };
+ var peg$startRuleFunction = peg$parseSnippet;
+
+ var peg$c0 = "$";
+ var peg$c1 = "${";
+ var peg$c2 = "}";
+ var peg$c3 = "|";
+ var peg$c4 = ",";
+ var peg$c5 = ":";
+ var peg$c6 = "/";
+ var peg$c7 = "/upcase";
+ var peg$c8 = "/downcase";
+ var peg$c9 = "/capitalize";
+ var peg$c10 = "+";
+ var peg$c11 = "-";
+ var peg$c12 = "?";
+ var peg$c13 = "\\";
+
+ var peg$r0 = /^[gimsuy]/;
+ var peg$r1 = /^[0-9]/;
+
+ var peg$e0 = peg$literalExpectation("$", false);
+ var peg$e1 = peg$literalExpectation("${", false);
+ var peg$e2 = peg$literalExpectation("}", false);
+ var peg$e3 = peg$anyExpectation();
+ var peg$e4 = peg$literalExpectation("|", false);
+ var peg$e5 = peg$literalExpectation(",", false);
+ var peg$e6 = peg$literalExpectation(":", false);
+ var peg$e7 = peg$literalExpectation("/", false);
+ var peg$e8 = peg$classExpectation(["g", "i", "m", "s", "u", "y"], false, false);
+ var peg$e9 = peg$literalExpectation("/upcase", false);
+ var peg$e10 = peg$literalExpectation("/downcase", false);
+ var peg$e11 = peg$literalExpectation("/capitalize", false);
+ var peg$e12 = peg$literalExpectation("+", false);
+ var peg$e13 = peg$literalExpectation("-", false);
+ var peg$e14 = peg$literalExpectation("?", false);
+ var peg$e15 = peg$classExpectation([["0", "9"]], false, false);
+ var peg$e16 = peg$literalExpectation("\\", false);
+
+ var peg$f0 = function(body) { return new Snippet(body) };
+ var peg$f1 = function(id) { return new Expression(id) };
+ var peg$f2 = function(id, content) { return new Placeholder(id, content) };
+ var peg$f3 = function(id, transformation) { return new Transformation(id, transformation) };
+ var peg$f4 = function(id, choices) { return new Choice(id, choices) };
+ var peg$f5 = function(char) { return /[\p{L}\d_]/u.test(char) };
+ var peg$f6 = function(first, rest) { return [...rest, first] };
+ var peg$f7 = function() { return escape(',|') };
+ var peg$f8 = function() { /*{*/ return escape('$}') };
+ var peg$f9 = function() { return escape('/') };
+ var peg$f10 = function() { return escape('$/') };
+ var peg$f11 = function() { return [''] };
+ var peg$f12 = function() { /*{*/ return escape('}') };
+ var peg$f13 = function() { return toUpper };
+ var peg$f14 = function() { return toLower };
+ var peg$f15 = function() { return upperFirst };
+ var peg$f16 = function(if_) { return [if_, ''] };
+ var peg$f17 = function(else_) { return ['', else_] };
+ var peg$f18 = function() { return escape(':') };
+ var peg$f19 = function(int) { return Number.parseInt(int) };
+ var peg$f20 = function(char) { return !escaped(char) };
+ var peg$f21 = function(chars) { return chars.join('') };
+ var peg$f22 = function() { return escapes.pop() };
+ var peg$f23 = function(char) { return escaped(char) };
+
+ var peg$currPos = 0;
+ var peg$savedPos = 0;
+ var peg$posDetailsCache = [{ line: 1, column: 1 }];
+ var peg$expected = [];
+ var peg$silentFails = 0;
+
+ var peg$result;
+
+ if ("startRule" in options) {
+ if (!(options.startRule in peg$startRuleFunctions)) {
+ throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
+ }
+
+ peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
+ }
+
+ function text() {
+ return input.substring(peg$savedPos, peg$currPos);
+ }
+
+ function offset() {
+ return peg$savedPos;
+ }
+
+ function range() {
+ return [peg$savedPos, peg$currPos];
+ }
+
+ function location() {
+ return peg$computeLocation(peg$savedPos, peg$currPos);
+ }
+
+ function expected(description, location) {
+ location = location !== undefined
+ ? location
+ : peg$computeLocation(peg$savedPos, peg$currPos);
+
+ throw peg$buildStructuredError(
+ [peg$otherExpectation(description)],
+ input.substring(peg$savedPos, peg$currPos),
+ location
+ );
+ }
+
+ function error(message, location) {
+ location = location !== undefined
+ ? location
+ : peg$computeLocation(peg$savedPos, peg$currPos);
+
+ throw peg$buildSimpleError(message, location);
+ }
+
+ function peg$literalExpectation(text, ignoreCase) {
+ return { type: "literal", text: text, ignoreCase: ignoreCase };
+ }
+
+ function peg$classExpectation(parts, inverted, ignoreCase) {
+ return { type: "class", parts: parts, inverted: inverted, ignoreCase: ignoreCase };
+ }
+
+ function peg$anyExpectation() {
+ return { type: "any" };
+ }
+
+ function peg$endExpectation() {
+ return { type: "end" };
+ }
+
+ function peg$otherExpectation(description) {
+ return { type: "other", description: description };
+ }
+
+ function peg$computePosDetails(pos) {
+ var details = peg$posDetailsCache[pos];
+ var p;
+
+ if (details) {
+ return details;
+ } else {
+ p = pos - 1;
+ while (!peg$posDetailsCache[p]) {
+ p--;
+ }
+
+ details = peg$posDetailsCache[p];
+ details = {
+ line: details.line,
+ column: details.column
+ };
+
+ while (p < pos) {
+ if (input.charCodeAt(p) === 10) {
+ details.line++;
+ details.column = 1;
+ } else {
+ details.column++;
+ }
+
+ p++;
+ }
+
+ peg$posDetailsCache[pos] = details;
+
+ return details;
+ }
+ }
+
+ var peg$VALIDFILENAME = typeof options.filename === "string" && options.filename.length > 0;
+ function peg$computeLocation(startPos, endPos) {
+ var loc = {};
+
+ if ( peg$VALIDFILENAME ) loc.filename = options.filename;
+
+ var startPosDetails = peg$computePosDetails(startPos);
+ loc.start = {
+ offset: startPos,
+ line: startPosDetails.line,
+ column: startPosDetails.column
+ };
+
+ var endPosDetails = peg$computePosDetails(endPos);
+ loc.end = {
+ offset: endPos,
+ line: endPosDetails.line,
+ column: endPosDetails.column
+ };
+
+ return loc;
+ }
+
+ function peg$begin() {
+ peg$expected.push({ pos: peg$currPos, variants: [] });
+ }
+
+ function peg$expect(expected) {
+ var top = peg$expected[peg$expected.length - 1];
+
+ if (peg$currPos < top.pos) { return; }
+
+ if (peg$currPos > top.pos) {
+ top.pos = peg$currPos;
+ top.variants = [];
+ }
+
+ top.variants.push(expected);
+ }
+
+ function peg$end(invert) {
+ var expected = peg$expected.pop();
+ var top = peg$expected[peg$expected.length - 1];
+ var variants = expected.variants;
+
+ if (top.pos !== expected.pos) { return; }
+
+ if (invert) {
+ variants = variants.map(function(e) {
+ return e.type === "not" ? e.expected : { type: "not", expected: e };
+ });
+ }
+
+ Array.prototype.push.apply(top.variants, variants);
+ }
+
+ function peg$buildSimpleError(message, location) {
+ return new peg$SyntaxError(message, null, null, location);
+ }
+
+ function peg$buildStructuredError(expected, found, location) {
+ return new peg$SyntaxError(
+ peg$SyntaxError.buildMessage(expected, found, location),
+ expected,
+ found,
+ location
+ );
+ }
+
+ function peg$buildError() {
+ var expected = peg$expected[0];
+ var failPos = expected.pos;
+
+ return peg$buildStructuredError(
+ expected.variants,
+ failPos < input.length ? input.charAt(failPos) : null,
+ failPos < input.length
+ ? peg$computeLocation(failPos, failPos + 1)
+ : peg$computeLocation(failPos, failPos)
+ );
+ }
+
+ function peg$parseSnippet() {
+ var s0, s1, s2;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ s0 = peg$currPos;
+ s1 = [];
+ s2 = peg$parseExpression();
+ if (s2 === peg$FAILED) {
+ s2 = peg$parseString();
+ }
+ if (s2 !== peg$FAILED) {
+ while (s2 !== peg$FAILED) {
+ s1.push(s2);
+ s2 = peg$parseExpression();
+ if (s2 === peg$FAILED) {
+ s2 = peg$parseString();
+ }
+ }
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$f0(s1);
+ }
+ s0 = s1;
+
+ return s0;
+ }
+
+ function peg$parseExpression() {
+ var s0, s1, s2, s3, s4;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ s0 = peg$currPos;
+ rule$expects(peg$e0);
+ if (input.charCodeAt(peg$currPos) === 36) {
+ s1 = peg$c0;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseInt();
+ if (s2 === peg$FAILED) {
+ s2 = peg$parseVariable();
+ }
+ if (s2 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s0 = peg$f1(s2);
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ rule$expects(peg$e1);
+ if (input.substr(peg$currPos, 2) === peg$c1) {
+ s1 = peg$c1;
+ peg$currPos += 2;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseInt();
+ if (s2 === peg$FAILED) {
+ s2 = peg$parseVariable();
+ }
+ if (s2 !== peg$FAILED) {
+ rule$expects(peg$e2);
+ if (input.charCodeAt(peg$currPos) === 125) {
+ s3 = peg$c2;
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ }
+ if (s3 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s0 = peg$f1(s2);
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ rule$expects(peg$e1);
+ if (input.substr(peg$currPos, 2) === peg$c1) {
+ s1 = peg$c1;
+ peg$currPos += 2;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseInt();
+ if (s2 === peg$FAILED) {
+ s2 = peg$parseVariable();
+ }
+ if (s2 !== peg$FAILED) {
+ s3 = peg$parsePlaceholder();
+ if (s3 !== peg$FAILED) {
+ rule$expects(peg$e2);
+ if (input.charCodeAt(peg$currPos) === 125) {
+ s4 = peg$c2;
+ peg$currPos++;
+ } else {
+ s4 = peg$FAILED;
+ }
+ if (s4 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s0 = peg$f2(s2, s3);
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ rule$expects(peg$e1);
+ if (input.substr(peg$currPos, 2) === peg$c1) {
+ s1 = peg$c1;
+ peg$currPos += 2;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseVariable();
+ if (s2 !== peg$FAILED) {
+ s3 = peg$parseTransformation();
+ if (s3 !== peg$FAILED) {
+ rule$expects(peg$e2);
+ if (input.charCodeAt(peg$currPos) === 125) {
+ s4 = peg$c2;
+ peg$currPos++;
+ } else {
+ s4 = peg$FAILED;
+ }
+ if (s4 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s0 = peg$f3(s2, s3);
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ rule$expects(peg$e1);
+ if (input.substr(peg$currPos, 2) === peg$c1) {
+ s1 = peg$c1;
+ peg$currPos += 2;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseInt();
+ if (s2 !== peg$FAILED) {
+ s3 = peg$parseChoice();
+ if (s3 !== peg$FAILED) {
+ rule$expects(peg$e2);
+ if (input.charCodeAt(peg$currPos) === 125) {
+ s4 = peg$c2;
+ peg$currPos++;
+ } else {
+ s4 = peg$FAILED;
+ }
+ if (s4 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s0 = peg$f4(s2, s3);
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ }
+ }
+ }
+ }
+
+ return s0;
+ }
+
+ function peg$parseVariable() {
+ var s0, s1, s2, s3, s4;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ s0 = peg$currPos;
+ s1 = [];
+ s2 = peg$currPos;
+ rule$expects(peg$e3);
+ if (input.length > peg$currPos) {
+ s3 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ }
+ if (s3 !== peg$FAILED) {
+ peg$savedPos = peg$currPos;
+ s4 = peg$f5(s3);
+ if (s4) {
+ s4 = undefined;
+ } else {
+ s4 = peg$FAILED;
+ }
+ if (s4 !== peg$FAILED) {
+ s3 = [s3, s4];
+ s2 = s3;
+ } else {
+ peg$currPos = s2;
+ s2 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s2;
+ s2 = peg$FAILED;
+ }
+ if (s2 !== peg$FAILED) {
+ while (s2 !== peg$FAILED) {
+ s1.push(s2);
+ s2 = peg$currPos;
+ rule$expects(peg$e3);
+ if (input.length > peg$currPos) {
+ s3 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ }
+ if (s3 !== peg$FAILED) {
+ peg$savedPos = peg$currPos;
+ s4 = peg$f5(s3);
+ if (s4) {
+ s4 = undefined;
+ } else {
+ s4 = peg$FAILED;
+ }
+ if (s4 !== peg$FAILED) {
+ s3 = [s3, s4];
+ s2 = s3;
+ } else {
+ peg$currPos = s2;
+ s2 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s2;
+ s2 = peg$FAILED;
+ }
+ }
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s0 = input.substring(s0, peg$currPos);
+ } else {
+ s0 = s1;
+ }
+
+ return s0;
+ }
+
+ function peg$parseChoice() {
+ var s0, s1, s2, s3, s4, s5, s6;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ s0 = peg$currPos;
+ rule$expects(peg$e4);
+ if (input.charCodeAt(peg$currPos) === 124) {
+ s1 = peg$c3;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseSelection();
+ if (s2 !== peg$FAILED) {
+ s3 = [];
+ s4 = peg$currPos;
+ rule$expects(peg$e5);
+ if (input.charCodeAt(peg$currPos) === 44) {
+ s5 = peg$c4;
+ peg$currPos++;
+ } else {
+ s5 = peg$FAILED;
+ }
+ if (s5 !== peg$FAILED) {
+ s6 = peg$parseSelection();
+ if (s6 !== peg$FAILED) {
+ s4 = s6;
+ } else {
+ peg$currPos = s4;
+ s4 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s4;
+ s4 = peg$FAILED;
+ }
+ while (s4 !== peg$FAILED) {
+ s3.push(s4);
+ s4 = peg$currPos;
+ rule$expects(peg$e5);
+ if (input.charCodeAt(peg$currPos) === 44) {
+ s5 = peg$c4;
+ peg$currPos++;
+ } else {
+ s5 = peg$FAILED;
+ }
+ if (s5 !== peg$FAILED) {
+ s6 = peg$parseSelection();
+ if (s6 !== peg$FAILED) {
+ s4 = s6;
+ } else {
+ peg$currPos = s4;
+ s4 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s4;
+ s4 = peg$FAILED;
+ }
+ }
+ rule$expects(peg$e4);
+ if (input.charCodeAt(peg$currPos) === 124) {
+ s4 = peg$c3;
+ peg$currPos++;
+ } else {
+ s4 = peg$FAILED;
+ }
+ if (s4 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s0 = peg$f6(s2, s3);
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parseSelection() {
+ var s0, s1, s2, s3;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ s0 = peg$currPos;
+ peg$savedPos = peg$currPos;
+ s1 = peg$f7();
+ if (s1) {
+ s1 = undefined;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseString();
+ if (s2 !== peg$FAILED) {
+ s3 = peg$parseEOL();
+ if (s3 !== peg$FAILED) {
+ s0 = s2;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parsePlaceholder() {
+ var s0, s1, s2, s3, s4;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ s0 = peg$currPos;
+ rule$expects(peg$e6);
+ if (input.charCodeAt(peg$currPos) === 58) {
+ s1 = peg$c5;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = peg$currPos;
+ s2 = peg$f8();
+ if (s2) {
+ s2 = undefined;
+ } else {
+ s2 = peg$FAILED;
+ }
+ if (s2 !== peg$FAILED) {
+ s3 = peg$parseSnippet();
+ if (s3 !== peg$FAILED) {
+ s4 = peg$parseEOL();
+ if (s4 !== peg$FAILED) {
+ s0 = s3;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parseTransformation() {
+ var s0, s1, s2, s3, s4, s5, s6, s7, s8;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ s0 = peg$currPos;
+ rule$expects(peg$e7);
+ if (input.charCodeAt(peg$currPos) === 47) {
+ s1 = peg$c6;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseRegExp();
+ if (s2 !== peg$FAILED) {
+ rule$expects(peg$e7);
+ if (input.charCodeAt(peg$currPos) === 47) {
+ s3 = peg$c6;
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ }
+ if (s3 !== peg$FAILED) {
+ s4 = peg$parseFormat();
+ if (s4 !== peg$FAILED) {
+ rule$expects(peg$e7);
+ if (input.charCodeAt(peg$currPos) === 47) {
+ s5 = peg$c6;
+ peg$currPos++;
+ } else {
+ s5 = peg$FAILED;
+ }
+ if (s5 !== peg$FAILED) {
+ s6 = peg$currPos;
+ s7 = [];
+ rule$expects(peg$e8);
+ if (peg$r0.test(input.charAt(peg$currPos))) {
+ s8 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s8 = peg$FAILED;
+ }
+ while (s8 !== peg$FAILED) {
+ s7.push(s8);
+ rule$expects(peg$e8);
+ if (peg$r0.test(input.charAt(peg$currPos))) {
+ s8 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s8 = peg$FAILED;
+ }
+ }
+ s6 = input.substring(s6, peg$currPos);
+ s0 = [ s2, s4, s6 ];
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parseRegExp() {
+ var s0, s1, s2, s3;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ s0 = peg$currPos;
+ peg$savedPos = peg$currPos;
+ s1 = peg$f9();
+ if (s1) {
+ s1 = undefined;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseString();
+ if (s2 !== peg$FAILED) {
+ s3 = peg$parseEOL();
+ if (s3 !== peg$FAILED) {
+ s0 = s2;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parseFormat() {
+ var s0, s1, s2, s3;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ s0 = peg$currPos;
+ peg$savedPos = peg$currPos;
+ s1 = peg$f10();
+ if (s1) {
+ s1 = undefined;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = [];
+ s3 = peg$parseInsert();
+ if (s3 === peg$FAILED) {
+ s3 = peg$parseString();
+ }
+ if (s3 !== peg$FAILED) {
+ while (s3 !== peg$FAILED) {
+ s2.push(s3);
+ s3 = peg$parseInsert();
+ if (s3 === peg$FAILED) {
+ s3 = peg$parseString();
+ }
+ }
+ } else {
+ s2 = peg$FAILED;
+ }
+ if (s2 !== peg$FAILED) {
+ s3 = peg$parseEOL();
+ if (s3 !== peg$FAILED) {
+ s0 = s2;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ s1 = '';
+ peg$savedPos = s0;
+ s1 = peg$f11();
+ s0 = s1;
+ }
+
+ return s0;
+ }
+
+ function peg$parseInsert() {
+ var s0, s1, s2, s3, s4, s5, s6, s7;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ s0 = peg$currPos;
+ rule$expects(peg$e0);
+ if (input.charCodeAt(peg$currPos) === 36) {
+ s1 = peg$c0;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseInt();
+ if (s2 !== peg$FAILED) {
+ s0 = s2;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ rule$expects(peg$e1);
+ if (input.substr(peg$currPos, 2) === peg$c1) {
+ s1 = peg$c1;
+ peg$currPos += 2;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseInt();
+ if (s2 !== peg$FAILED) {
+ rule$expects(peg$e2);
+ if (input.charCodeAt(peg$currPos) === 125) {
+ s3 = peg$c2;
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ }
+ if (s3 !== peg$FAILED) {
+ s0 = s2;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ rule$expects(peg$e1);
+ if (input.substr(peg$currPos, 2) === peg$c1) {
+ s1 = peg$c1;
+ peg$currPos += 2;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseInt();
+ if (s2 !== peg$FAILED) {
+ rule$expects(peg$e6);
+ if (input.charCodeAt(peg$currPos) === 58) {
+ s3 = peg$c5;
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ }
+ if (s3 !== peg$FAILED) {
+ peg$savedPos = peg$currPos;
+ s4 = peg$f12();
+ if (s4) {
+ s4 = undefined;
+ } else {
+ s4 = peg$FAILED;
+ }
+ if (s4 !== peg$FAILED) {
+ s5 = peg$parseTransform();
+ if (s5 !== peg$FAILED) {
+ s6 = peg$parseEOL();
+ if (s6 !== peg$FAILED) {
+ rule$expects(peg$e2);
+ if (input.charCodeAt(peg$currPos) === 125) {
+ s7 = peg$c2;
+ peg$currPos++;
+ } else {
+ s7 = peg$FAILED;
+ }
+ if (s7 !== peg$FAILED) {
+ s0 = [ s2, s5 ];
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ }
+ }
+
+ return s0;
+ }
+
+ function peg$parseTransform() {
+ var s0, s1, s2, s3, s4, s5, s6;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ s0 = peg$currPos;
+ rule$expects(peg$e9);
+ if (input.substr(peg$currPos, 7) === peg$c7) {
+ s1 = peg$c7;
+ peg$currPos += 7;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$f13();
+ }
+ s0 = s1;
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ rule$expects(peg$e10);
+ if (input.substr(peg$currPos, 9) === peg$c8) {
+ s1 = peg$c8;
+ peg$currPos += 9;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$f14();
+ }
+ s0 = s1;
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ rule$expects(peg$e11);
+ if (input.substr(peg$currPos, 11) === peg$c9) {
+ s1 = peg$c9;
+ peg$currPos += 11;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$f15();
+ }
+ s0 = s1;
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ rule$expects(peg$e12);
+ if (input.charCodeAt(peg$currPos) === 43) {
+ s1 = peg$c10;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseString();
+ if (s2 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s0 = peg$f16(s2);
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ rule$expects(peg$e13);
+ if (input.charCodeAt(peg$currPos) === 45) {
+ s1 = peg$c11;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 === peg$FAILED) {
+ s1 = null;
+ }
+ s2 = peg$parseString();
+ if (s2 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s0 = peg$f17(s2);
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ rule$expects(peg$e14);
+ if (input.charCodeAt(peg$currPos) === 63) {
+ s1 = peg$c12;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = peg$currPos;
+ s2 = peg$f18();
+ if (s2) {
+ s2 = undefined;
+ } else {
+ s2 = peg$FAILED;
+ }
+ if (s2 !== peg$FAILED) {
+ s3 = peg$parseString();
+ if (s3 !== peg$FAILED) {
+ s4 = peg$parseEOL();
+ if (s4 !== peg$FAILED) {
+ rule$expects(peg$e6);
+ if (input.charCodeAt(peg$currPos) === 58) {
+ s5 = peg$c5;
+ peg$currPos++;
+ } else {
+ s5 = peg$FAILED;
+ }
+ if (s5 !== peg$FAILED) {
+ s6 = peg$parseString();
+ if (s6 !== peg$FAILED) {
+ s0 = [ s3, s6 ];
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return s0;
+ }
+
+ function peg$parseInt() {
+ var s0, s1, s2, s3;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ s0 = peg$currPos;
+ s1 = peg$currPos;
+ s2 = [];
+ rule$expects(peg$e15);
+ if (peg$r1.test(input.charAt(peg$currPos))) {
+ s3 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ }
+ if (s3 !== peg$FAILED) {
+ while (s3 !== peg$FAILED) {
+ s2.push(s3);
+ rule$expects(peg$e15);
+ if (peg$r1.test(input.charAt(peg$currPos))) {
+ s3 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ }
+ }
+ } else {
+ s2 = peg$FAILED;
+ }
+ if (s2 !== peg$FAILED) {
+ s1 = input.substring(s1, peg$currPos);
+ } else {
+ s1 = s2;
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$f19(s1);
+ }
+ s0 = s1;
+
+ return s0;
+ }
+
+ function peg$parseString() {
+ var s0, s1, s2, s3, s4;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ s0 = peg$currPos;
+ s1 = [];
+ s2 = peg$currPos;
+ s3 = peg$parseEscape();
+ if (s3 !== peg$FAILED) {
+ s2 = s3;
+ } else {
+ peg$currPos = s2;
+ s2 = peg$FAILED;
+ }
+ if (s2 === peg$FAILED) {
+ s2 = peg$currPos;
+ rule$expects(peg$e3);
+ if (input.length > peg$currPos) {
+ s3 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ }
+ if (s3 !== peg$FAILED) {
+ peg$savedPos = peg$currPos;
+ s4 = peg$f20(s3);
+ if (s4) {
+ s4 = undefined;
+ } else {
+ s4 = peg$FAILED;
+ }
+ if (s4 !== peg$FAILED) {
+ s2 = s3;
+ } else {
+ peg$currPos = s2;
+ s2 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s2;
+ s2 = peg$FAILED;
+ }
+ }
+ if (s2 !== peg$FAILED) {
+ while (s2 !== peg$FAILED) {
+ s1.push(s2);
+ s2 = peg$currPos;
+ s3 = peg$parseEscape();
+ if (s3 !== peg$FAILED) {
+ s2 = s3;
+ } else {
+ peg$currPos = s2;
+ s2 = peg$FAILED;
+ }
+ if (s2 === peg$FAILED) {
+ s2 = peg$currPos;
+ rule$expects(peg$e3);
+ if (input.length > peg$currPos) {
+ s3 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ }
+ if (s3 !== peg$FAILED) {
+ peg$savedPos = peg$currPos;
+ s4 = peg$f20(s3);
+ if (s4) {
+ s4 = undefined;
+ } else {
+ s4 = peg$FAILED;
+ }
+ if (s4 !== peg$FAILED) {
+ s2 = s3;
+ } else {
+ peg$currPos = s2;
+ s2 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s2;
+ s2 = peg$FAILED;
+ }
+ }
+ }
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$f21(s1);
+ }
+ s0 = s1;
+
+ return s0;
+ }
+
+ function peg$parseEOL() {
+ var s0;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ peg$savedPos = peg$currPos;
+ s0 = peg$f22();
+ if (s0) {
+ s0 = undefined;
+ } else {
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parseEscape() {
+ var s0, s1, s2, s3;
+
+ var rule$expects = function (expected) {
+ if (peg$silentFails === 0) peg$expect(expected);
+ }
+
+ s0 = peg$currPos;
+ rule$expects(peg$e16);
+ if (input.charCodeAt(peg$currPos) === 92) {
+ s1 = peg$c13;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ rule$expects(peg$e3);
+ if (input.length > peg$currPos) {
+ s2 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ }
+ if (s2 !== peg$FAILED) {
+ peg$savedPos = peg$currPos;
+ s3 = peg$f23(s2);
+ if (s3) {
+ s3 = undefined;
+ } else {
+ s3 = peg$FAILED;
+ }
+ if (s3 !== peg$FAILED) {
+ s0 = s2;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+
+ const toUpper = string => string.toLocaleUpperCase()
+ const toLower = string => string.toLocaleLowerCase()
+ const upperFirst = string => string.replace(/^\p{CWU}/u, char => toUpper(char))
+
+ // Handle and allow certain characters to be escaped in certain contexts
+ const escapes = ['$']
+ const escape = iterable => escapes.push(iterable)
+ const escaped = value => escapes[escapes.length - 1].includes(value)
+
+
+ peg$begin();
+ peg$result = peg$startRuleFunction();
+
+ if (peg$result !== peg$FAILED && peg$currPos === input.length) {
+ return peg$result;
+ } else {
+ if (peg$result !== peg$FAILED && peg$currPos < input.length) {
+ peg$expect(peg$endExpectation());
+ }
+
+ throw peg$buildError();
+ }
+}
+
+module.exports = {
+ SyntaxError: peg$SyntaxError,
+ parse: peg$parse
+};
diff --git a/parser/snippet-body-parser.pegjs b/parser/snippet-body-parser.pegjs
new file mode 100644
index 00000000..c6a13d0a
--- /dev/null
+++ b/parser/snippet-body-parser.pegjs
@@ -0,0 +1,58 @@
+{
+ const toUpper = string => string.toLocaleUpperCase()
+ const toLower = string => string.toLocaleLowerCase()
+ const upperFirst = string => string.replace(/^\p{CWU}/u, char => toUpper(char))
+
+ // Handle and allow certain characters to be escaped in certain contexts
+ const escapes = ['$']
+ const escape = iterable => escapes.push(iterable)
+ const escaped = value => escapes[escapes.length - 1].includes(value)
+}
+
+Snippet = body:(Expression / String)+ { return new Snippet(body) }
+
+Expression
+ = "$" id:(Tabstop / Variable) { return new Expression(id) }
+ / "${" id:(Tabstop / Variable) "}" { return new Expression(id) }
+ / "${" id:(Tabstop / Variable) content:Placeholder "}" { return new Placeholder(id, content) }
+ / "${" id:Variable transformation:Transformation "}" { return new Transformation(id, transformation) }
+ / "${" id:Tabstop choices:Choice "}" { return new Choice(id, choices) }
+
+Tabstop = Int
+
+Variable = $(char:. & { return /[\p{L}\d_]/u.test(char) })+
+
+Choice = "|" first:Selection rest:("," @Selection)* "|" { return [...rest, first] }
+
+Selection = & { return escape(',|') } @String EOL
+
+Placeholder = ":" & { /*{*/ return escape('$}') } @Snippet EOL
+
+Transformation = "/" @RegExp "/" @Format "/" @$[gimsuy]*
+
+RegExp = & { return escape('/') } @String EOL
+
+Format
+ = & { return escape('$/') } @(Insert / String)+ EOL
+ / "" { return [''] }
+
+Insert
+ = '$' @Int
+ / '${' @Int '}'
+ / '${' @Int ':' & { /*{*/ return escape('}') } @Transform EOL '}'
+
+Transform
+ = '/upcase' { return toUpper }
+ / '/downcase' { return toLower }
+ / '/capitalize' { return upperFirst }
+ / '+' if_:String { return [if_, ''] }
+ / '-'? else_:String { return ['', else_] }
+ / '?' & { return escape(':') } @String EOL ':' @String
+
+Int = int:$[0-9]+ { return Number.parseInt(int) }
+
+String = chars:(@Escape / @char:. & { return !escaped(char) })+ { return chars.join('') }
+
+EOL = & { return escapes.pop() }
+
+Escape = "\\" @char:. & { return escaped(char) }
diff --git a/snippets.js b/snippets.js
new file mode 100644
index 00000000..baefc9ef
--- /dev/null
+++ b/snippets.js
@@ -0,0 +1,145 @@
+const { CompositeDisposable, File } = require('atom')
+
+const CSON = require('season')
+const path = require('path')
+
+const { promises: fs } = require('fs')
+
+const ScopedPropertyStore = require('scoped-property-store')
+
+const AvailableSnippetsView = require('./available-snippets-view')
+
+const parser = require('./parser/snippet-body-parser.js')
+
+// TODO: Convert private arrow functions into methods once atom supports them
+module.exports = class Snippets {
+ static #disposables = new CompositeDisposable()
+ // This needs to be made available now even if we reconstruct it on activation
+ // as service objects can potentially access it before that happens
+ static #snippetsByScopes = new ScopedPropertyStore()
+ static #snippetsByPackage = new WeakMap()
+
+ static #userSnippetsFile
+ static #userSnippetsBasename = path.join(atom.getConfigDirPath(), 'snippets')
+
+ static #userSnippetsURI = 'atom://.atom/snippets'
+
+ // TODO: Uncomment once atom supports private methods
+ // static get #userSnippetsPath () {
+ // // See #loadUserSnippets
+ // return this.#userSnippetsFile.getPath()
+ // }
+
+ static snippets () {
+ // Consider having a static frozen object and not creating a new one each
+ // call, as modifying the service object is often a mistake / bad practice
+ return {
+ parse: string => parser.parse(string),
+ // TODO: Drop 'snippets' prefix 'snippets.snippetsByScopes' is too verbose
+ snippetsByScopes: () => this.#snippetsByScopes,
+ snippetsByPackage: () => this.#snippetsByPackage,
+ // Returns the path _currently in use_
+ userSnippetsPath: () => this.#userSnippetsFile.getPath()
+ }
+ }
+
+ static async activate () {
+ // As there's no built-in way to be notified when package activation is
+ // complete, the loading of package snippets has to be started synchronously
+ // (before our activationPromise resolves) so that service consumers can
+ // reliably access the generated promises.
+ const promises = atom.packages.getLoadedPackages().map(pack =>
+ this.#snippetsByPackage.set(pack, this.#loadPackage(pack)).get(pack))
+
+ // The above also applies to '#userSnippetsFile' and '#userSnippetsPath'
+ promises.push(this.#loadUserSnippets({ dispose: () => {} }))
+
+ this.#disposables.add(
+ atom.workspace.addOpener(uri => uri === this.#userSnippetsURI &&
+ atom.workspace.open(this.#userSnippetsFile.getPath())),
+ atom.packages.onDidLoadPackage(pack =>
+ this.#snippetsByPackage.set(pack, this.#loadPackage(pack))),
+ atom.config.observe('core.packagesWithSnippetsDisabled', packs =>
+ this.#togglePackages(new Set(packs))),
+ atom.commands.add('atom-text-editor', 'snippets:available', event =>
+ new AvailableSnippetsView(this.snippets(), event.currentTarget.getModel())))
+
+ await Promise.all(promises)
+ }
+
+ static deactivate () {
+ this.#disposables.dispose()
+ }
+
+ static #readSnippets = async (filepath) => {
+ try {
+ return await new Promise((resolve, reject) =>
+ CSON.readFile(filepath, (error, object) =>
+ error == null ? resolve(object) : reject(error)))
+ } catch (error) {
+ atom.notifications.addWarning(`Unable to load snippets from: '${filepath}'`, {
+ description: 'Make sure you have permissions to access the directory and file.',
+ detail: error.toString(),
+ stack: error.stack,
+ dismissable: true
+ })
+ return {}
+ }
+ }
+
+ // Also updates the user snippets file
+ static #loadUserSnippets = async (oldSnippets, priority = 1) => {
+ // Remove old user defined snippets
+ oldSnippets.dispose()
+
+ this.#userSnippetsFile = new File(`${this.#userSnippetsBasename}.json`)
+ if (!(await this.#userSnippetsFile.exists())) {
+ this.#userSnippetsFile = new File(`${this.#userSnippetsBasename}.cson`)
+ }
+ await this.#userSnippetsFile.create()
+ const snippets = await this.#readSnippets(this.#userSnippetsFile.getPath())
+
+ const disposable = new CompositeDisposable(
+ this.#snippetsByScopes.addProperties(this.#userSnippetsFile.getPath(), snippets, { priority }),
+ this.#userSnippetsFile.onDidChange(() => this.#loadUserSnippets(disposable)),
+ this.#userSnippetsFile.onDidDelete(() => this.#loadUserSnippets(disposable)),
+ this.#userSnippetsFile.onDidRename(() => this.#loadUserSnippets(disposable)),
+ { dispose: () => this.#disposables.remove(disposable) })
+
+ this.#disposables.add(disposable)
+ }
+
+ static #loadPackage = async (pack) => {
+ const directory = path.join(pack.path, 'snippets')
+ try {
+ const files = await fs.readdir(directory)
+ const snippets = files.map(file => this.#readSnippets(path.join(directory, file)))
+ // Reduces the snippets into a single object
+ return Object.assign(...await Promise.all(snippets))
+ } catch (error) {
+ if (error.code !== 'ENOTDIR' && error.code !== 'ENOENT') {
+ atom.notifications.addError(`Error reading snippets directory ${directory}`, {
+ description: 'Make sure you have permissions to access the directory.',
+ detail: error.toString(),
+ stack: error.stack,
+ dismissable: true
+ })
+ }
+ // Path either doesn't exist, or isn't a directory
+ return {}
+ }
+ }
+
+ static #togglePackages = (packs) => {
+ // Technically we could compute and toggle only the packages that were
+ // enabled / disabled, but that would result in more complex code and often
+ // be slower because of how many iterations 'ScopedPropertyStore' would make
+ // over its own internal data structures. Thus we just reset
+ this.#snippetsByScopes = new ScopedPropertyStore()
+ // (Eventually) Reconstruct the whole scoped snippet storage
+ atom.packages.getLoadedPackages()
+ .filter(({ name }) => !packs.has(name))
+ .forEach(pack => this.#snippetsByPackage.get(pack).then(snippets =>
+ this.#snippetsByScopes.addProperties(pack.path, snippets)))
+ }
+}
diff --git a/lib/snippets.cson b/snippets/snippets.cson
similarity index 100%
rename from lib/snippets.cson
rename to snippets/snippets.cson
diff --git a/spec/body-parser-spec.js b/spec/body-parser-spec.js
index 35492ded..0a4953b1 100644
--- a/spec/body-parser-spec.js
+++ b/spec/body-parser-spec.js
@@ -1,86 +1,103 @@
-const BodyParser = require('../lib/snippet-body-parser');
+/* eslint no-template-curly-in-string: 0 */
-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(`\
+const fs = require('fs-plus')
+const peg = require('pegjs')
+
+const Snippet = require('../lib/snippet')
+const Variable = require('../lib/variable')
+const VariableRegistery = require('../lib/variable-registery')
+
+const grammar = fs.readFileSync(require.resolve('../lib/snippet-body-parser.pegjs'), 'utf8')
+
+describe('Snippet Body Parser', () => {
+ beforeEach(() => {
+ this.registery = new VariableRegistery()
+ this.bodyParser = peg.generate(grammar, { context: { Snippet, Variable, registery: this.registery } })
+ })
+ it('breaks a snippet body into lines, with each line containing tab stops at the appropriate position', () => {
+ const snippet = this.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"
- ]);
- });
+ expect(snippet).toEqual(new Snippet({
+ registery: this.registery,
+ range: [0, 61],
+ value: [
+ 'the quick brown ',
+ this.registery.add(new Variable({ identifier: '1', range: [16, 18], value: [] })),
+ 'fox ',
+ new Variable({
+ identifier: '2',
+ range: [22, 44],
+ value: [
+ 'jumped ',
+ this.registery.add(new Variable({ identifier: '3', range: [33, 42], value: ['over'] })),
+ '\n'
+ ]
+ }),
+ 'the ',
+ this.registery.add(new Variable({ identifier: '4', range: [48, 57], value: ['lazy'] })),
+ ' dog'
+ ]
+ }))
+ })
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}}");
+ const bodyTree = this.bodyParser.parse('module ${1:ActiveRecord::${TM_FILENAME/(?:\\A|_)([A-Za-z0-9]+)(?:\\.rb)?/(?2::\\u$1)/g}}')
expect(bodyTree).toEqual([
- "module ",
+ 'module ',
{
- "index": 1,
- "content": ["ActiveRecord::", ""]
+ index: 1,
+ content: ['ActiveRecord::', '']
}
- ]);
- });
+ ])
+ })
- it("skips escaped tabstops", () => {
- const bodyTree = BodyParser.parse("snippet $1 escaped \\$2 \\\\$3");
+ it('skips escaped tabstops', () => {
+ const bodyTree = this.bodyParser.parse('snippet $1 escaped \\$2 \\\\$3')
expect(bodyTree).toEqual([
- "snippet ",
+ 'snippet ',
{
index: 1,
content: []
},
- " escaped $2 \\",
+ ' escaped $2 \\',
{
index: 3,
content: []
}
- ]);
- });
+ ])
+ })
- it("includes escaped right-braces", () => {
- const bodyTree = BodyParser.parse("snippet ${1:{\\}}");
+ it('includes escaped right-braces', () => {
+ const bodyTree = this.bodyParser.parse('snippet ${1:{\\}}')
expect(bodyTree).toEqual([
- "snippet ",
+ 'snippet ',
{
index: 1,
- content: ["{}"]
+ content: ['{}']
}
- ]);
- });
+ ])
+ })
- it("parses a snippet with transformations", () => {
- const bodyTree = BodyParser.parse("<${1:p}>$0${1/f/F/}>");
+ it('parses a snippet with transformations', () => {
+ const bodyTree = this.bodyParser.parse('<${1:p}>$0${1/f/F/}>')
expect(bodyTree).toEqual([
'<',
- {index: 1, content: ['p']},
+ { index: 1, content: ['p'] },
'>',
- {index: 0, content: []},
+ { index: 0, content: [] },
'',
- {index: 1, content: [], substitution: {find: /f/g, replace: ['F']}},
+ { index: 1, content: [], substitution: { find: /f/g, replace: ['F'] } },
'>'
- ]);
- });
+ ])
+ })
- 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");
+ it('parses a snippet with multiple tab stops with transformations', () => {
+ const bodyTree = this.bodyParser.parse('${1:placeholder} ${1/(.)/\\u$1/} $1 ${2:ANOTHER} ${2/^(.*)$/\\L$1/} $2')
expect(bodyTree).toEqual([
- {index: 1, content: ['placeholder']},
+ { index: 1, content: ['placeholder'] },
' ',
{
index: 1,
@@ -88,15 +105,15 @@ the quick brown $1fox \${2:jumped \${3:over}
substitution: {
find: /(.)/g,
replace: [
- {escape: 'u'},
- {backreference: 1}
+ { escape: 'u' },
+ { backreference: 1 }
]
}
},
' ',
- {index: 1, content: []},
+ { index: 1, content: [] },
' ',
- {index: 2, content: ['ANOTHER']},
+ { index: 2, content: ['ANOTHER'] },
' ',
{
index: 2,
@@ -104,21 +121,20 @@ the quick brown $1fox \${2:jumped \${3:over}
substitution: {
find: /^(.*)$/g,
replace: [
- {escape: 'L'},
- {backreference: 1}
+ { escape: 'L' },
+ { backreference: 1 }
]
}
},
' ',
- {index: 2, content: []},
- ]);
- });
+ { index: 2, content: [] }
+ ])
+ })
-
- it("parses a snippet with transformations and mirrors", () => {
- const bodyTree = BodyParser.parse("${1:placeholder}\n${1/(.)/\\u$1/}\n$1");
+ it('parses a snippet with transformations and mirrors', () => {
+ const bodyTree = this.bodyParser.parse('${1:placeholder}\n${1/(.)/\\u$1/}\n$1')
expect(bodyTree).toEqual([
- {index: 1, content: ['placeholder']},
+ { index: 1, content: ['placeholder'] },
'\n',
{
index: 1,
@@ -126,23 +142,23 @@ the quick brown $1fox \${2:jumped \${3:over}
substitution: {
find: /(.)/g,
replace: [
- {escape: 'u'},
- {backreference: 1}
+ { escape: 'u' },
+ { backreference: 1 }
]
}
},
'\n',
- {index: 1, content: []}
- ]);
- });
+ { index: 1, content: [] }
+ ])
+ })
- it("parses a snippet with a format string and case-control flags", () => {
- const bodyTree = BodyParser.parse("<${1:p}>$0${1/(.)(.*)/\\u$1$2/}>");
+ it('parses a snippet with a format string and case-control flags', () => {
+ const bodyTree = this.bodyParser.parse('<${1:p}>$0${1/(.)(.*)/\\u$1$2/}>')
expect(bodyTree).toEqual([
'<',
- {index: 1, content: ['p']},
+ { index: 1, content: ['p'] },
'>',
- {index: 0, content: []},
+ { index: 0, content: [] },
'',
{
index: 1,
@@ -150,25 +166,25 @@ the quick brown $1fox \${2:jumped \${3:over}
substitution: {
find: /(.)(.*)/g,
replace: [
- {escape: 'u'},
- {backreference: 1},
- {backreference: 2}
+ { escape: 'u' },
+ { backreference: 1 },
+ { backreference: 2 }
]
}
},
'>'
- ]);
- });
+ ])
+ })
- it("parses a snippet with an escaped forward slash in a transform", () => {
+ 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${1/(.)\\/(.*)/\\u$1$2/}>");
+ const bodyTree = this.bodyParser.parse('<${1:p}>$0${1/(.)\\/(.*)/\\u$1$2/}>')
expect(bodyTree).toEqual([
'<',
- {index: 1, content: ['p']},
+ { index: 1, content: ['p'] },
'>',
- {index: 0, content: []},
+ { index: 0, content: [] },
'',
{
index: 1,
@@ -176,52 +192,54 @@ the quick brown $1fox \${2:jumped \${3:over}
substitution: {
find: /(.)\/(.*)/g,
replace: [
- {escape: 'u'},
- {backreference: 1},
- {backreference: 2}
+ { escape: 'u' },
+ { backreference: 1 },
+ { backreference: 2 }
]
}
},
'>'
- ]);
- });
+ ])
+ })
it("parses a snippet with a placeholder that mirrors another tab stop's content", () => {
- const bodyTree = BodyParser.parse("$4console.${3:log}('${2:$1}', $1);$0");
+ const bodyTree = this.bodyParser.parse("$4console.${3:log}('${2:$1}', $1);$0")
expect(bodyTree).toEqual([
- {index: 4, content: []},
+ { index: 4, content: [] },
'console.',
- {index: 3, content: ['log']},
+ { index: 3, content: ['log'] },
'(\'',
{
- index: 2, content: [
- {index: 1, content: []}
+ index: 2,
+ content: [
+ { index: 1, content: [] }
]
},
'\', ',
- {index: 1, content: []},
+ { index: 1, content: [] },
');',
- {index: 0, content: []}
- ]);
- });
+ { index: 0, content: [] }
+ ])
+ })
- 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");
+ it('parses a snippet with a placeholder that mixes text and tab stop references', () => {
+ const bodyTree = this.bodyParser.parse("$4console.${3:log}('${2:uh $1}', $1);$0")
expect(bodyTree).toEqual([
- {index: 4, content: []},
+ { index: 4, content: [] },
'console.',
- {index: 3, content: ['log']},
+ { index: 3, content: ['log'] },
'(\'',
{
- index: 2, content: [
+ index: 2,
+ content: [
'uh ',
- {index: 1, content: []}
+ { index: 1, content: [] }
]
},
'\', ',
- {index: 1, content: []},
+ { index: 1, content: [] },
');',
- {index: 0, content: []}
- ]);
- });
-});
+ { index: 0, content: [] }
+ ])
+ })
+})
diff --git a/spec/insertion-spec.js b/spec/insertion-spec.js
index 83fac925..e2a9d4a4 100644
--- a/spec/insertion-spec.js
+++ b/spec/insertion-spec.js
@@ -5,30 +5,30 @@ const range = new Range(0, 0)
describe('Insertion', () => {
it('returns what it was given when it has no substitution', () => {
- let insertion = new Insertion({
+ const insertion = new Insertion({
range,
substitution: undefined
})
- let transformed = insertion.transform('foo!')
+ const transformed = insertion.transform('foo!')
expect(transformed).toEqual('foo!')
})
it('transforms what it was given when it has a regex transformation', () => {
- let insertion = new Insertion({
+ const insertion = new Insertion({
range,
substitution: {
find: /foo/g,
replace: ['bar']
}
})
- let transformed = insertion.transform('foo!')
+ const transformed = insertion.transform('foo!')
expect(transformed).toEqual('bar!')
})
it('transforms the case of the next character when encountering a \\u or \\l flag', () => {
- let uInsertion = new Insertion({
+ const uInsertion = new Insertion({
range,
substitution: {
find: /(.)(.)(.*)/g,
@@ -45,7 +45,7 @@ describe('Insertion', () => {
expect(uInsertion.transform('fOo!')).toEqual('fOo!')
expect(uInsertion.transform('FOO!')).toEqual('FOO!')
- let lInsertion = new Insertion({
+ const lInsertion = new Insertion({
range,
substitution: {
find: /(.{2})(.)(.*)/g,
@@ -65,7 +65,7 @@ describe('Insertion', () => {
})
it('transforms the case of all remaining characters when encountering a \\U or \\L flag, up until it sees a \\E flag', () => {
- let uInsertion = new Insertion({
+ const uInsertion = new Insertion({
range,
substitution: {
find: /(.)(.*)/,
@@ -81,7 +81,7 @@ describe('Insertion', () => {
expect(uInsertion.transform('lOREM IPSUM!')).toEqual('lOREM IPSUM!')
expect(uInsertion.transform('LOREM IPSUM!')).toEqual('LOREM IPSUM!')
- let ueInsertion = new Insertion({
+ const ueInsertion = new Insertion({
range,
substitution: {
find: /(.)(.{3})(.*)/,
@@ -99,7 +99,7 @@ describe('Insertion', () => {
expect(ueInsertion.transform('lOREm ipsum!')).toEqual('lOREm ipsum!')
expect(ueInsertion.transform('LOREM IPSUM!')).toEqual('LOREM IPSUM!')
- let lInsertion = new Insertion({
+ const lInsertion = new Insertion({
range,
substitution: {
find: /(.{4})(.)(.*)/,
@@ -114,7 +114,7 @@ describe('Insertion', () => {
expect(lInsertion.transform('LOREM IPSUM!')).toEqual('LOREmwhat')
- let leInsertion = new Insertion({
+ const leInsertion = new Insertion({
range,
substitution: {
find: /^([A-Fa-f])(.*)(.)$/,
diff --git a/spec/snippet-loading-spec.js b/spec/snippet-loading-spec.js
index d6c5dd18..a544d73c 100644
--- a/spec/snippet-loading-spec.js
+++ b/spec/snippet-loading-spec.js
@@ -1,130 +1,130 @@
-const path = require('path');
-const fs = require('fs-plus');
-const temp = require('temp').track();
+const path = require('path')
+const fs = require('fs-plus')
+const temp = require('temp').track()
-describe("Snippet Loading", () => {
- let configDirPath, snippetsService;
+describe('Snippet Loading', () => {
+ let configDirPath, snippetsService
beforeEach(() => {
- configDirPath = temp.mkdirSync('atom-config-dir-');
- spyOn(atom, 'getConfigDirPath').andReturn(configDirPath);
+ configDirPath = temp.mkdirSync('atom-config-dir-')
+ spyOn(atom, 'getConfigDirPath').andReturn(configDirPath)
- spyOn(console, 'warn');
- if (atom.notifications != null) { spyOn(atom.notifications, 'addError'); }
+ spyOn(console, 'warn')
+ if (atom.notifications != null) { spyOn(atom.notifications, 'addError') }
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')),
- ]);
- });
+ atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-broken-snippets'))
+ ])
+ })
afterEach(() => {
- waitsForPromise(() => Promise.resolve(atom.packages.deactivatePackages('snippets')));
+ waitsForPromise(() => Promise.resolve(atom.packages.deactivatePackages('snippets')))
runs(() => {
- jasmine.unspy(atom.packages, 'getLoadedPackages');
- });
- });
+ jasmine.unspy(atom.packages, 'getLoadedPackages')
+ })
+ })
const activateSnippetsPackage = () => {
- waitsForPromise(() => atom.packages.activatePackage("snippets").then(({mainModule}) => {
- snippetsService = mainModule.provideSnippets();
- mainModule.loaded = false;
- }));
+ waitsForPromise(() => atom.packages.activatePackage('snippets').then(({ mainModule }) => {
+ snippetsService = mainModule.provideSnippets()
+ mainModule.loaded = false
+ }))
- waitsFor("all snippets to load", 3000, () => snippetsService.bundledSnippetsLoaded());
- };
+ waitsFor('all snippets to load', 3000, () => snippetsService.bundledSnippetsLoaded())
+ }
- it("loads the bundled snippet template snippets", () => {
- activateSnippetsPackage();
+ it('loads the bundled snippet template snippets', () => {
+ activateSnippetsPackage()
runs(() => {
- 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':");
- expect(csonSnippet.tabStopList.length).toBeGreaterThan(0);
- });
- });
-
- it("loads non-hidden snippet files from atom packages with snippets directories", () => {
- activateSnippetsPackage();
+ 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':")
+ expect(csonSnippet.tabStopList.length).toBeGreaterThan(0)
+ })
+ })
+
+ it('loads non-hidden snippet files from atom packages with snippets directories', () => {
+ activateSnippetsPackage()
runs(() => {
- 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');
- expect(snippet.rightLabelHTML).toBe('Label');
- });
- });
-
- it("logs a warning if package snippets files cannot be parsed", () => {
- activateSnippetsPackage();
+ 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')
+ 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/);
- });
- });
+ expect(console.warn.calls.length).toBeGreaterThan(0)
+ expect(console.warn.mostRecentCall.args[0]).toMatch(/Error reading.*package-with-broken-snippets/)
+ })
+ })
- describe("::loadPackageSnippets(callback)", () => {
+ 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.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"));
+ waitsForPromise(() => atom.packages.activatePackage('language-javascript'))
- activateSnippetsPackage();
+ activateSnippetsPackage()
runs(() => {
- const 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", () => {
- let 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", () => {
+ const 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', () => {
+ let 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'), `\
{
@@ -136,24 +136,24 @@ describe("Snippet Loading", () => {
}
}\
`
- );
- activateSnippetsPackage();
- });
+ )
+ activateSnippetsPackage()
+ })
- it("loads the snippets from that file", () => {
- let snippet = null;
+ it('loads the snippets from that file', () => {
+ let snippet = null
- waitsFor(() => snippet = snippetsService.snippetsForScopes(['.foo'])['foo']);
+ 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", () => {
+ 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": {
@@ -164,23 +164,23 @@ describe("Snippet Loading", () => {
}
}\
`
- );
+ )
- waitsFor("snippets to be changed", () => {
- const snippet = snippetsService.snippetsForScopes(['.foo'])['foo'];
- return snippet && snippet.body === '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'), "");
- });
+ fs.writeFileSync(path.join(configDirPath, 'snippets.json'), '')
+ })
- waitsFor("snippets to be removed", () => !snippetsService.snippetsForScopes(['.foo'])['foo']);
- });
- });
- });
+ waitsFor('snippets to be removed', () => !snippetsService.snippetsForScopes(['.foo']).foo)
+ })
+ })
+ })
- describe("when ~/.atom/snippets.cson exists", () => {
+ describe('when ~/.atom/snippets.cson exists', () => {
beforeEach(() => {
fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), `\
".foo":
@@ -188,122 +188,122 @@ describe("Snippet Loading", () => {
"prefix": "foo"
"body": "bar1"\
`
- );
- activateSnippetsPackage();
- });
+ )
+ activateSnippetsPackage()
+ })
- it("loads the snippets from that file", () => {
- let snippet = null;
+ it('loads the snippets from that file', () => {
+ let snippet = null
- waitsFor(() => snippet = snippetsService.snippetsForScopes(['.foo'])['foo']);
+ 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", () => {
+ 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", () => {
- const snippet = snippetsService.snippetsForScopes(['.foo'])['foo'];
- return snippet && snippet.body === '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'), "");
- });
+ fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), '')
+ })
- waitsFor("snippets to be removed", () => {
- const snippet = snippetsService.snippetsForScopes(['.foo'])['foo'];
- return snippet == null;
- });
- });
- });
- });
+ waitsFor('snippets to be removed', () => {
+ const snippet = snippetsService.snippetsForScopes(['.foo']).foo
+ return snippet == null
+ })
+ })
+ })
+ })
- it("notifies the user when the user snippets file cannot be loaded", () => {
- fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), '".junk":::');
+ it('notifies the user when the user snippets file cannot be loaded', () => {
+ fs.writeFileSync(path.join(configDirPath, 'snippets.cson'), '".junk":::')
- activateSnippetsPackage();
+ activateSnippetsPackage()
runs(() => {
- expect(console.warn).toHaveBeenCalled();
+ expect(console.warn).toHaveBeenCalled()
if (atom.notifications != null) {
- expect(atom.notifications.addError).toHaveBeenCalled();
+ expect(atom.notifications.addError).toHaveBeenCalled()
}
- });
- });
+ })
+ })
- 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', []);
+ 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();
+ activateSnippetsPackage()
runs(() => {
- const snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']);
- expect(Object.keys(snippets).length).toBe(1);
- atom.config.set('core.packagesWithSnippetsDisabled', originalConfig);
- });
- });
+ const 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", () => {
- let originalConfig = atom.config.get('core.packagesWithSnippetsDisabled');
- atom.config.set('core.packagesWithSnippetsDisabled', []);
+ const originalConfig = atom.config.get('core.packagesWithSnippetsDisabled')
+ atom.config.set('core.packagesWithSnippetsDisabled', [])
- activateSnippetsPackage();
+ activateSnippetsPackage()
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);
- atom.config.set('core.packagesWithSnippetsDisabled', originalConfig);
- });
- });
+ 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)
+ atom.config.set('core.packagesWithSnippetsDisabled', originalConfig)
+ })
+ })
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']);
+ const originalConfig = atom.config.get('core.packagesWithSnippetsDisabled')
+ atom.config.set('core.packagesWithSnippetsDisabled', ['package-with-snippets'])
- activateSnippetsPackage();
+ activateSnippetsPackage()
runs(() => {
- const snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']);
- expect(Object.keys(snippets).length).toBe(0);
- atom.config.set('core.packagesWithSnippetsDisabled', originalConfig);
- });
- });
+ const 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", () => {
- const originalConfig = atom.config.get('core.packagesWithSnippetsDisabled');
- atom.config.set('core.packagesWithSnippetsDisabled', []);
+ 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();
+ activateSnippetsPackage()
runs(() => {
- let snippets = snippetsService.snippetsForScopes(['.package-with-snippets-unique-scope']);
- expect(Object.keys(snippets).length).toBe(1);
+ 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);
+ 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);
- });
- });
- });
-});
+ 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.js b/spec/snippets-spec.js
index 68994478..9c1fae84 100644
--- a/spec/snippets-spec.js
+++ b/spec/snippets-spec.js
@@ -1,152 +1,154 @@
-const path = require('path');
-const temp = require('temp').track();
-const Snippets = require('../lib/snippets');
-const {TextEditor} = require('atom');
+/* eslint no-template-curly-in-string: 0 */
-describe("Snippets extension", () => {
- let editorElement, editor;
+const path = require('path')
+const temp = require('temp').track()
+const Snippets = require('../lib/snippets')
+const { TextEditor } = require('atom')
+
+describe('Snippets extension', () => {
+ let editorElement, editor
const simulateTabKeyEvent = (param) => {
if (param == null) {
- param = {};
+ param = {}
}
- const {shift} = param;
- const event = atom.keymaps.constructor.buildKeydownEvent('tab', {shift, target: editorElement});
- atom.keymaps.handleKeyboardEvent(event);
- };
+ const { shift } = param
+ const event = atom.keymaps.constructor.buildKeydownEvent('tab', { shift, target: editorElement })
+ atom.keymaps.handleKeyboardEvent(event)
+ }
beforeEach(() => {
- spyOn(Snippets, 'loadAll');
- spyOn(Snippets, 'getUserSnippetsPath').andReturn('');
+ spyOn(Snippets, 'loadAll')
+ spyOn(Snippets, 'getUserSnippetsPath').andReturn('')
- waitsForPromise(() => atom.workspace.open('sample.js'));
- waitsForPromise(() => atom.packages.activatePackage('language-javascript'));
- waitsForPromise(() => atom.packages.activatePackage('snippets'));
+ 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);
- });
- });
+ editor = atom.workspace.getActiveTextEditor()
+ editorElement = atom.views.getView(editor)
+ })
+ })
afterEach(() => {
- waitsForPromise(() => atom.packages.deactivatePackage('snippets'));
- });
+ waitsForPromise(() => atom.packages.deactivatePackage('snippets'))
+ })
- describe("provideSnippets interface", () => {
- let snippetsInterface = null;
+ describe('provideSnippets interface', () => {
+ let snippetsInterface = null
beforeEach(() => {
- snippetsInterface = Snippets.provideSnippets();
- });
+ 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);
- });
+ 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);
+ 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'));
+ 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 {}", () => {
- const snippets = atom.packages.getActivePackage('snippets').mainModule;
- expect(snippets.snippetToExpandUnderCursor(editor)).toEqual(false);
- });
-
- it("ignores invalid snippets in the config", () => {
- 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};
- expect(snippets.getSnippets(editor)).toEqual({});
- });
-
- describe("when null snippets are present", () => {
+ 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 {}', () => {
+ const snippets = atom.packages.getActivePackage('snippets').mainModule
+ expect(snippets.snippetToExpandUnderCursor(editor)).toEqual(false)
+ })
+
+ it('ignores invalid snippets in the config', () => {
+ 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 }
+ 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': {
+ 'some snippet': {
+ prefix: 't1',
+ body: 'this is a test'
}
},
- ".source.js .nope": {
- "some snippet": {
- prefix: "t1",
+ '.source.js .nope': {
+ 'some snippet': {
+ prefix: 't1',
body: null
}
}
- }));
+ }))
- it("overrides the less-specific defined snippet", () => {
- const snippets = Snippets.provideSnippets();
- expect(snippets.snippetsForScopes(['.source.js'])['t1']).toBeTruthy();
- expect(snippets.snippetsForScopes(['.source.js .nope.not-today'])['t1']).toBeFalsy();
- });
- });
+ it('overrides the less-specific defined snippet', () => {
+ const 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"
+ '.source.js': {
+ 'without tab stops': {
+ prefix: 't1',
+ body: 'this is a test'
},
- "with only an end tab stop": {
- prefix: "t1a",
- body: "something $0 strange"
+ 'with only an end tab stop': {
+ prefix: 't1a',
+ body: 'something $0 strange'
},
- "overlapping prefix": {
- prefix: "tt1",
- body: "this is another test"
+ 'overlapping prefix': {
+ prefix: 'tt1',
+ body: 'this is another test'
},
- "special chars": {
- prefix: "@unique",
- body: "@unique see"
+ 'special chars': {
+ prefix: '@unique',
+ body: '@unique see'
},
- "tab stops": {
- prefix: "t2",
+ 'tab stops': {
+ prefix: 't2',
body: `\
go here next:($2) and finally go here:($0)
go here first:($1)
@@ -154,8 +156,8 @@ go here first:($1)
`
},
- "indented second line": {
- prefix: "t3",
+ 'indented second line': {
+ prefix: 't3',
body: `\
line 1
\tline 2$1
@@ -163,16 +165,16 @@ $2\
`
},
- "multiline with indented placeholder tabstop": {
- prefix: "t4",
+ 'multiline with indented placeholder tabstop': {
+ prefix: 't4',
body: `\
line \${1:1}
\${2:body...}\
`
},
- "multiline starting with tabstop": {
- prefix: "t4b",
+ 'multiline starting with tabstop': {
+ prefix: 't4b',
body: `\
$1 = line 1 {
line 2
@@ -180,31 +182,31 @@ $1 = line 1 {
`
},
- "nested tab stops": {
- prefix: "t5",
+ 'nested tab stops': {
+ prefix: 't5',
body: '${1:"${2:key}"}: ${3:value}'
},
- "caused problems with undo": {
- prefix: "t6",
+ 'caused problems with undo': {
+ prefix: 't6',
body: `\
first line$1
\${2:placeholder ending second line}\
`
},
- "tab stops at beginning and then end of snippet": {
- prefix: "t6b",
- body: "$1expanded$0"
+ 'tab stops at beginning and then end of snippet': {
+ prefix: 't6b',
+ body: '$1expanded$0'
},
- "tab stops at end and then beginning of snippet": {
- prefix: "t6c",
- body: "$0expanded$1"
+ 'tab stops at end and then beginning of snippet': {
+ prefix: 't6c',
+ body: '$0expanded$1'
},
- "contains empty lines": {
- prefix: "t7",
+ 'contains empty lines': {
+ prefix: 't7',
body: `\
first line $1
@@ -212,24 +214,24 @@ first line $1
fourth line after blanks $2\
`
},
- "with/without placeholder": {
- prefix: "t8",
+ 'with/without placeholder': {
+ prefix: 't8',
body: `\
with placeholder \${1:test}
without placeholder \${2}\
`
},
- "multi-caret": {
- prefix: "t9",
+ 'multi-caret': {
+ prefix: 't9',
body: `\
with placeholder \${1:test}
without placeholder $1\
`
},
- "multi-caret-multi-tabstop": {
- prefix: "t9b",
+ 'multi-caret-multi-tabstop': {
+ prefix: 't9b',
body: `\
with placeholder \${1:test}
without placeholder $1
@@ -238,974 +240,966 @@ third tabstop $3\
`
},
- "large indices": {
- prefix: "t10",
- body: "hello${10} ${11:large} indices${1}"
+ 'large indices': {
+ prefix: 't10',
+ body: 'hello${10} ${11:large} indices${1}'
},
- "no body": {
- prefix: "bad1"
+ 'no body': {
+ prefix: 'bad1'
},
- "number body": {
- prefix: "bad2",
+ 'number body': {
+ prefix: 'bad2',
body: 100
},
- "many tabstops": {
- prefix: "t11",
- body: "$0one${1} ${2:two} three${3}"
+ 'many tabstops': {
+ prefix: 't11',
+ body: '$0one${1} ${2:two} three${3}'
},
- "simple transform": {
- prefix: "t12",
- body: "[${1:b}][/${1/[ ]+.*$//}]"
+ 'simple transform': {
+ prefix: 't12',
+ body: '[${1:b}][/${1/[ ]+.*$//}]'
},
- "transform with non-transforming mirrors": {
- prefix: "t13",
- body: "${1:placeholder}\n${1/(.)/\\u$1/}\n$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"
+ '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": {
+ 'has a transformed tab stop without a corresponding ordinary tab stop': {
prefix: 't15',
- body: "${1/(.)/\\u$1/} & $2"
+ body: '${1/(.)/\\u$1/} & $2'
},
- "has a transformed tab stop that occurs before the corresponding ordinary tab stop": {
+ 'has a transformed tab stop that occurs before the corresponding ordinary tab stop': {
prefix: 't16',
- body: "& ${1/(.)/\\u$1/} & ${1:q}"
+ body: '& ${1/(.)/\\u$1/} & ${1:q}'
},
"has a placeholder that mirrors another tab stop's content": {
prefix: 't17',
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": {
+ '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();
+ it('parses snippets once, reusing cached ones on subsequent queries', () => {
+ // TODO: Once Jasmin >= 2.6.1, ensure the property is invoked when appropriate
+ editor.insertText('t1')
+ simulateTabKeyEvent()
- editor.setText("");
- editor.insertText("t1");
- simulateTabKeyEvent();
+ expect(editor.lineTextForBufferRow(0)).toBe('this is a testvar quicksort = function () {')
+ expect(editor.getCursorScreenPosition()).toEqual([0, 14])
- expect(Snippets.getBodyParser).not.toHaveBeenCalled();
- expect(editor.lineTextForBufferRow(0)).toBe("this is a test");
- expect(editor.getCursorScreenPosition()).toEqual([0, 14]);
+ editor.setText('')
+ editor.insertText('t1')
+ simulateTabKeyEvent()
- Snippets.getBodyParser.reset();
+ expect(editor.lineTextForBufferRow(0)).toBe('this is a test')
+ expect(editor.getCursorScreenPosition()).toEqual([0, 14])
Snippets.add(__filename, {
- ".source.js": {
- "invalidate previous snippet": {
- prefix: "t1",
- body: "new snippet"
+ '.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", () => {
+ })
+
+ editor.setText('')
+ editor.insertText('t1')
+ simulateTabKeyEvent()
+
+ 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", () => {
- 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]]);
+ 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([[2, 14], [2, 17]]) // should highlight text typed at tab stop
- simulateTabKeyEvent({shift: true});
- expect(editor.getSelectedBufferRange()).toEqual([[3, 15], [3, 15]]);
+ 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]);
+ 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]);
+ 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", () => {
+ editor.setCursorScreenPosition([4, 0])
+ editor.insertText('t2')
+ simulateTabKeyEvent()
+ simulateTabKeyEvent()
+
+ editor.moveRight()
+ simulateTabKeyEvent({ shift: true })
+ expect(editor.getCursorBufferPosition()).toEqual([4, 15])
+ })
+ })
+
+ describe('when the cursor is moved within the bounds of the current tab stop', () => {
+ it('should not terminate the snippet', () => {
+ editor.setCursorScreenPosition([0, 0])
+ editor.insertText('t8')
+ simulateTabKeyEvent()
+
+ expect(editor.lineTextForBufferRow(0)).toBe('with placeholder test')
+ editor.moveRight()
+ editor.moveLeft()
+ editor.insertText('foo')
+ expect(editor.lineTextForBufferRow(0)).toBe('with placeholder tesfoot')
+
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(1)).toBe('without placeholder var quicksort = function () {')
+ editor.insertText('test')
+ expect(editor.lineTextForBufferRow(1)).toBe('without placeholder testvar quicksort = function () {')
+ editor.moveLeft()
+ editor.insertText('foo')
+ expect(editor.lineTextForBufferRow(1)).toBe('without placeholder tesfootvar quicksort = function () {')
+ })
+ })
+
+ describe('when the backspace is press within the bounds of the current tab stop', () => {
+ it('should not terminate the snippet', () => {
+ editor.setCursorScreenPosition([0, 0])
+ editor.insertText('t8')
+ simulateTabKeyEvent()
+
+ expect(editor.lineTextForBufferRow(0)).toBe('with placeholder test')
+ editor.moveRight()
+ editor.backspace()
+ editor.insertText('foo')
+ expect(editor.lineTextForBufferRow(0)).toBe('with placeholder tesfoo')
+
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(1)).toBe('without placeholder var quicksort = function () {')
+ editor.insertText('test')
+ expect(editor.lineTextForBufferRow(1)).toBe('without placeholder testvar quicksort = function () {')
+ editor.backspace()
+ editor.insertText('foo')
+ expect(editor.lineTextForBufferRow(1)).toBe('without placeholder tesfoovar quicksort = function () {')
+ })
+ })
+ })
+
+ describe('when the snippet contains hard tabs', () => {
+ describe('when the edit session is in soft-tabs mode', () => {
+ it('translates hard tabs in the snippet to the appropriate number of spaces', () => {
+ expect(editor.getSoftTabs()).toBeTruthy()
+ editor.insertText('t3')
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(1)).toBe(' line 2')
+ expect(editor.getCursorBufferPosition()).toEqual([1, 8])
+ })
+ })
+
+ describe('when the edit session is in hard-tabs mode', () => {
+ it('inserts hard tabs in the snippet directly', () => {
+ editor.setSoftTabs(false)
+ editor.insertText('t3')
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(1)).toBe('\tline 2')
+ expect(editor.getCursorBufferPosition()).toEqual([1, 7])
+ })
+ })
+ })
+
+ describe('when the snippet prefix is indented', () => {
+ describe('when the snippet spans a single line', () => {
+ it('does not indent the next line', () => {
+ editor.setCursorScreenPosition([2, Infinity])
+ editor.insertText(' t1')
+ atom.commands.dispatch(editorElement, 'snippets:expand')
+ expect(editor.lineTextForBufferRow(3)).toBe(' var pivot = items.shift(), current, left = [], right = [];')
+ })
+ })
+
+ describe('when the snippet spans multiple lines', () => {
+ it('indents the subsequent lines of the snippet to be even with the start of the first line', () => {
+ expect(editor.getSoftTabs()).toBeTruthy()
+ editor.setCursorScreenPosition([2, Infinity])
+ editor.insertText(' t3')
+ atom.commands.dispatch(editorElement, 'snippets:expand')
+ expect(editor.lineTextForBufferRow(2)).toBe(' if (items.length <= 1) return items; line 1')
+ expect(editor.lineTextForBufferRow(3)).toBe(' line 2')
+ expect(editor.getCursorBufferPosition()).toEqual([3, 12])
+ })
+ })
+ })
+
+ describe('when the snippet spans multiple lines', () => {
beforeEach(() => {
- editor.update({autoIndent: true});
+ editor.update({ autoIndent: true })
// editor.update() returns a Promise that never gets resolved, so we
// need to return undefined to avoid a timeout in the spec.
// TODO: Figure out why `editor.update({autoIndent: true})` never gets resolved.
- });
-
- it("places tab stops correctly", () => {
- 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 () {");
- });
- });
+ })
+
+ it('places tab stops correctly', () => {
+ expect(editor.getSoftTabs()).toBeTruthy()
+ editor.setCursorScreenPosition([2, Infinity])
+ editor.insertText(' t3')
+ atom.commands.dispatch(editorElement, 'snippets:expand')
+ expect(editor.getCursorBufferPosition()).toEqual([3, 12])
+ atom.commands.dispatch(editorElement, 'snippets:next-tab-stop')
+ expect(editor.getCursorBufferPosition()).toEqual([4, 4])
+ })
+
+ it('indents the subsequent lines of the snippet based on the indent level before the snippet is inserted', () => {
+ editor.setCursorScreenPosition([2, Infinity])
+ editor.insertNewline()
+ editor.insertText('t4b')
+ atom.commands.dispatch(editorElement, 'snippets:expand')
+
+ expect(editor.lineTextForBufferRow(3)).toBe(' = line 1 {') // 4 + 1 spaces (because the tab stop is invisible)
+ expect(editor.lineTextForBufferRow(4)).toBe(' line 2')
+ expect(editor.lineTextForBufferRow(5)).toBe(' }')
+ expect(editor.getCursorBufferPosition()).toEqual([3, 4])
+ })
+
+ it('does not change the relative positioning of the tab stops when inserted multiple times', () => {
+ editor.setCursorScreenPosition([2, Infinity])
+ editor.insertNewline()
+ editor.insertText('t4')
+ atom.commands.dispatch(editorElement, 'snippets:expand')
+
+ expect(editor.getSelectedBufferRange()).toEqual([[3, 9], [3, 10]])
+ atom.commands.dispatch(editorElement, 'snippets:next-tab-stop')
+ expect(editor.getSelectedBufferRange()).toEqual([[4, 6], [4, 13]])
+
+ editor.insertText('t4')
+ atom.commands.dispatch(editorElement, 'snippets:expand')
+
+ expect(editor.getSelectedBufferRange()).toEqual([[4, 11], [4, 12]])
+ atom.commands.dispatch(editorElement, 'snippets:next-tab-stop')
+ expect(editor.getSelectedBufferRange()).toEqual([[5, 8], [5, 15]])
+
+ editor.setText('') // Clear editor
+ editor.insertText('t4')
+ atom.commands.dispatch(editorElement, 'snippets:expand')
+
+ expect(editor.getSelectedBufferRange()).toEqual([[0, 5], [0, 6]])
+ atom.commands.dispatch(editorElement, 'snippets:next-tab-stop')
+ expect(editor.getSelectedBufferRange()).toEqual([[1, 2], [1, 9]])
+ })
+ })
+
+ describe('when multiple snippets match the prefix', () => {
+ it('expands the snippet that is the longest match for the prefix', () => {
+ editor.insertText('t113')
+ expect(editor.getCursorScreenPosition()).toEqual([0, 4])
+
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('t113 var quicksort = function () {')
+ expect(editor.getCursorScreenPosition()).toEqual([0, 6])
+
+ editor.undo()
+ editor.undo()
+
+ editor.insertText('tt1')
+ expect(editor.getCursorScreenPosition()).toEqual([0, 3])
+
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('this is another testvar quicksort = function () {')
+ expect(editor.getCursorScreenPosition()).toEqual([0, 20])
+
+ editor.undo()
+ editor.undo()
+
+ editor.insertText('@t1')
+ expect(editor.getCursorScreenPosition()).toEqual([0, 3])
+
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('@this is a testvar quicksort = function () {')
+ expect(editor.getCursorScreenPosition()).toEqual([0, 15])
+ })
+ })
+ })
+
+ describe('when the word preceding the cursor ends with a snippet prefix', () => {
+ it('inserts a tab as normal', () => {
+ editor.insertText('t1t1t1')
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('t1t1t1 var quicksort = function () {')
+ })
+ })
describe("when the letters preceding the cursor don't match a snippet", () => {
- it("inserts a tab as normal", () => {
- editor.insertText("xxte");
- expect(editor.getCursorScreenPosition()).toEqual([0, 4]);
-
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(0)).toBe("xxte var quicksort = function () {");
- expect(editor.getCursorScreenPosition()).toEqual([0, 6]);
- });
- });
-
- describe("when text is selected", () => {
- it("inserts a tab as normal", () => {
- editor.insertText("t1");
- editor.setSelectedBufferRange([[0, 0], [0, 2]]);
-
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(0)).toBe(" t1var quicksort = function () {");
- expect(editor.getSelectedBufferRange()).toEqual([[0, 0], [0, 4]]);
- });
- });
-
- describe("when a previous snippet expansion has just been undone", () => {
- describe("when the tab stops appear in the middle of the snippet", () => {
+ it('inserts a tab as normal', () => {
+ editor.insertText('xxte')
+ expect(editor.getCursorScreenPosition()).toEqual([0, 4])
+
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('xxte var quicksort = function () {')
+ expect(editor.getCursorScreenPosition()).toEqual([0, 6])
+ })
+ })
+
+ describe('when text is selected', () => {
+ it('inserts a tab as normal', () => {
+ editor.insertText('t1')
+ editor.setSelectedBufferRange([[0, 0], [0, 2]])
+
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe(' t1var quicksort = function () {')
+ expect(editor.getSelectedBufferRange()).toEqual([[0, 0], [0, 4]])
+ })
+ })
+
+ describe('when a previous snippet expansion has just been undone', () => {
+ describe('when the tab stops appear in the middle of the snippet', () => {
it("expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", () => {
- editor.insertText('t6\n');
- editor.setCursorBufferPosition([0, 2]);
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(0)).toBe("first line");
- editor.undo();
- expect(editor.lineTextForBufferRow(0)).toBe("t6");
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(0)).toBe("first line");
- });
- });
-
- describe("when the tab stops appear at the beginning and then the end of snippet", () => {
+ editor.insertText('t6\n')
+ editor.setCursorBufferPosition([0, 2])
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('first line')
+ editor.undo()
+ expect(editor.lineTextForBufferRow(0)).toBe('t6')
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('first line')
+ })
+ })
+
+ describe('when the tab stops appear at the beginning and then the end of snippet', () => {
it("expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", () => {
- editor.insertText('t6b\n');
- editor.setCursorBufferPosition([0, 3]);
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(0)).toBe("expanded");
- editor.undo();
- expect(editor.lineTextForBufferRow(0)).toBe("t6b");
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(0)).toBe("expanded");
- expect(editor.getCursorBufferPosition()).toEqual([0, 0]);
- });
- });
-
- describe("when the tab stops appear at the end and then the beginning of snippet", () => {
+ editor.insertText('t6b\n')
+ editor.setCursorBufferPosition([0, 3])
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('expanded')
+ editor.undo()
+ expect(editor.lineTextForBufferRow(0)).toBe('t6b')
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('expanded')
+ expect(editor.getCursorBufferPosition()).toEqual([0, 0])
+ })
+ })
+
+ describe('when the tab stops appear at the end and then the beginning of snippet', () => {
it("expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", () => {
- editor.insertText('t6c\n');
- editor.setCursorBufferPosition([0, 3]);
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(0)).toBe("expanded");
- editor.undo();
- expect(editor.lineTextForBufferRow(0)).toBe("t6c");
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(0)).toBe("expanded");
- expect(editor.getCursorBufferPosition()).toEqual([0, 8]);
- });
- });
- });
-
- describe("when the prefix contains non-word characters", () => {
- it("selects the non-word characters as part of the prefix", () => {
- editor.insertText("@unique");
- expect(editor.getCursorScreenPosition()).toEqual([0, 7]);
-
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(0)).toBe("@unique seevar quicksort = function () {");
- expect(editor.getCursorScreenPosition()).toEqual([0, 11]);
-
- editor.setCursorBufferPosition([10, 0]);
- editor.insertText("'@unique");
-
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(10)).toBe("'@unique see");
- expect(editor.getCursorScreenPosition()).toEqual([10, 12]);
- });
-
- it("does not select the whitespace before the prefix", () => {
- editor.insertText("a; @unique");
- expect(editor.getCursorScreenPosition()).toEqual([0, 10]);
-
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(0)).toBe("a; @unique seevar quicksort = function () {");
- expect(editor.getCursorScreenPosition()).toEqual([0, 14]);
- });
- });
-
- describe("when snippet contains tabstops with or without placeholder", () => {
- it("should create two markers", () => {
- editor.setCursorScreenPosition([0, 0]);
- editor.insertText('t8');
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(0)).toBe("with placeholder test");
- expect(editor.lineTextForBufferRow(1)).toBe("without placeholder var quicksort = function () {");
-
- expect(editor.getSelectedBufferRange()).toEqual([[0, 17], [0, 21]]);
-
- simulateTabKeyEvent();
- expect(editor.getSelectedBufferRange()).toEqual([[1, 20], [1, 20]]);
- });
- });
-
- describe("when snippet contains multi-caret tabstops with or without placeholder", () => {
- it("should create two markers", () => {
- editor.setCursorScreenPosition([0, 0]);
- editor.insertText('t9');
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(0)).toBe("with placeholder test");
- expect(editor.lineTextForBufferRow(1)).toBe("without placeholder var quicksort = function () {");
- editor.insertText('hello');
- expect(editor.lineTextForBufferRow(0)).toBe("with placeholder hello");
- expect(editor.lineTextForBufferRow(1)).toBe("without placeholder hellovar quicksort = function () {");
- });
-
- it("terminates the snippet when cursors are destroyed", () => {
- editor.setCursorScreenPosition([0, 0]);
- editor.insertText('t9b');
- simulateTabKeyEvent();
- editor.getCursors()[0].destroy();
- editor.getCursorBufferPosition();
- simulateTabKeyEvent();
-
- expect(editor.lineTextForBufferRow(1)).toEqual("without placeholder ");
- });
-
- it("terminates the snippet expansion if a new cursor moves outside the bounds of the tab stops", () => {
- editor.setCursorScreenPosition([0, 0]);
- editor.insertText('t9b');
- simulateTabKeyEvent();
- editor.insertText('test');
-
- editor.getCursors()[0].destroy();
- editor.moveDown(); // this should destroy the previous expansion
- editor.moveToBeginningOfLine();
+ editor.insertText('t6c\n')
+ editor.setCursorBufferPosition([0, 3])
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('expanded')
+ editor.undo()
+ expect(editor.lineTextForBufferRow(0)).toBe('t6c')
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('expanded')
+ expect(editor.getCursorBufferPosition()).toEqual([0, 8])
+ })
+ })
+ })
+
+ describe('when the prefix contains non-word characters', () => {
+ it('selects the non-word characters as part of the prefix', () => {
+ editor.insertText('@unique')
+ expect(editor.getCursorScreenPosition()).toEqual([0, 7])
+
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('@unique seevar quicksort = function () {')
+ expect(editor.getCursorScreenPosition()).toEqual([0, 11])
+
+ editor.setCursorBufferPosition([10, 0])
+ editor.insertText("'@unique")
+
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(10)).toBe("'@unique see")
+ expect(editor.getCursorScreenPosition()).toEqual([10, 12])
+ })
+
+ it('does not select the whitespace before the prefix', () => {
+ editor.insertText('a; @unique')
+ expect(editor.getCursorScreenPosition()).toEqual([0, 10])
+
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('a; @unique seevar quicksort = function () {')
+ expect(editor.getCursorScreenPosition()).toEqual([0, 14])
+ })
+ })
+
+ describe('when snippet contains tabstops with or without placeholder', () => {
+ it('should create two markers', () => {
+ editor.setCursorScreenPosition([0, 0])
+ editor.insertText('t8')
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('with placeholder test')
+ expect(editor.lineTextForBufferRow(1)).toBe('without placeholder var quicksort = function () {')
+
+ expect(editor.getSelectedBufferRange()).toEqual([[0, 17], [0, 21]])
+
+ simulateTabKeyEvent()
+ expect(editor.getSelectedBufferRange()).toEqual([[1, 20], [1, 20]])
+ })
+ })
+
+ describe('when snippet contains multi-caret tabstops with or without placeholder', () => {
+ it('should create two markers', () => {
+ editor.setCursorScreenPosition([0, 0])
+ editor.insertText('t9')
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('with placeholder test')
+ expect(editor.lineTextForBufferRow(1)).toBe('without placeholder var quicksort = function () {')
+ editor.insertText('hello')
+ expect(editor.lineTextForBufferRow(0)).toBe('with placeholder hello')
+ expect(editor.lineTextForBufferRow(1)).toBe('without placeholder hellovar quicksort = function () {')
+ })
+
+ it('terminates the snippet when cursors are destroyed', () => {
+ editor.setCursorScreenPosition([0, 0])
+ editor.insertText('t9b')
+ simulateTabKeyEvent()
+ editor.getCursors()[0].destroy()
+ editor.getCursorBufferPosition()
+ simulateTabKeyEvent()
+
+ expect(editor.lineTextForBufferRow(1)).toEqual('without placeholder ')
+ })
+
+ it('terminates the snippet expansion if a new cursor moves outside the bounds of the tab stops', () => {
+ editor.setCursorScreenPosition([0, 0])
+ editor.insertText('t9b')
+ simulateTabKeyEvent()
+ editor.insertText('test')
+
+ editor.getCursors()[0].destroy()
+ editor.moveDown() // this should destroy the previous expansion
+ editor.moveToBeginningOfLine()
// this should insert whitespace instead of going through tabstops of the previous destroyed snippet
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(2).indexOf(" second")).toBe(0);
- });
+ 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');
+ 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 2')
- simulateTabKeyEvent();
- editor.insertText('line 3');
+ simulateTabKeyEvent()
+ editor.insertText('line 3')
- expect(editor.lineTextForBufferRow(2).indexOf("line 2 ")).toBe(-1);
- });
+ 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);
+ 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');
+ 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// ===");
+ )
+ })
+ })
+
+ 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();
-
- const cursors = editor.getCursors();
- expect(cursors.length).toEqual(3);
-
- expect(cursors[0].getBufferPosition()).toEqual([0, 3]);
- expect(cursors[1].getBufferPosition()).toEqual([7, 8]);
- expect(cursors[2].getBufferPosition()).toEqual([12, 5]);
- expect(cursors[0].selection.isEmpty()).toBe(true);
- expect(cursors[1].selection.isEmpty()).toBe(true);
- expect(cursors[2].selection.isEmpty()).toBe(true);
-
- simulateTabKeyEvent();
- expect(cursors[0].getBufferPosition()).toEqual([0, 7]);
- expect(cursors[1].getBufferPosition()).toEqual([7, 12]);
- expect(cursors[2].getBufferPosition()).toEqual([12, 9]);
- expect(cursors[0].selection.isEmpty()).toBe(false);
- expect(cursors[1].selection.isEmpty()).toBe(false);
- expect(cursors[2].selection.isEmpty()).toBe(false);
- expect(cursors[0].selection.getText()).toEqual('two');
- expect(cursors[1].selection.getText()).toEqual('two');
- expect(cursors[2].selection.getText()).toEqual('two');
-
- simulateTabKeyEvent();
- expect(cursors[0].getBufferPosition()).toEqual([0, 13]);
- expect(cursors[1].getBufferPosition()).toEqual([7, 18]);
- expect(cursors[2].getBufferPosition()).toEqual([12, 15]);
- expect(cursors[0].selection.isEmpty()).toBe(true);
- expect(cursors[1].selection.isEmpty()).toBe(true);
- expect(cursors[2].selection.isEmpty()).toBe(true);
-
- simulateTabKeyEvent();
- expect(cursors[0].getBufferPosition()).toEqual([0, 0]);
- expect(cursors[1].getBufferPosition()).toEqual([7, 5]);
- expect(cursors[2].getBufferPosition()).toEqual([12, 2]);
- expect(cursors[0].selection.isEmpty()).toBe(true);
- expect(cursors[1].selection.isEmpty()).toBe(true);
- expect(cursors[2].selection.isEmpty()).toBe(true);
- });
- });
- });
-
- describe("when the cursors do not share common snippet prefixes", () => {
- it("inserts tabs as normal", () => {
- editor.insertText('t9');
- editor.setCursorBufferPosition([12, 2]);
- editor.insertText(' t8');
- editor.addCursorAtBufferPosition([0, 2]);
- simulateTabKeyEvent();
- expect(editor.lineTextForBufferRow(0)).toBe("t9 var quicksort = function () {");
- expect(editor.lineTextForBufferRow(12)).toBe("}; t8 ");
- });
- });
-
- 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, 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();
- const configDirPath = temp.mkdirSync('atom-config-dir-');
- spyOn(atom, 'getConfigDirPath').andReturn(configDirPath);
- atom.workspace.open('atom://.atom/snippets');
-
- waitsFor(() => atom.workspace.getActiveTextEditor() != null);
+ 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()
+
+ const cursors = editor.getCursors()
+ expect(cursors.length).toEqual(3)
+
+ expect(cursors[0].getBufferPosition()).toEqual([0, 3])
+ expect(cursors[1].getBufferPosition()).toEqual([7, 8])
+ expect(cursors[2].getBufferPosition()).toEqual([12, 5])
+ expect(cursors[0].selection.isEmpty()).toBe(true)
+ expect(cursors[1].selection.isEmpty()).toBe(true)
+ expect(cursors[2].selection.isEmpty()).toBe(true)
+
+ simulateTabKeyEvent()
+ expect(cursors[0].getBufferPosition()).toEqual([0, 7])
+ expect(cursors[1].getBufferPosition()).toEqual([7, 12])
+ expect(cursors[2].getBufferPosition()).toEqual([12, 9])
+ expect(cursors[0].selection.isEmpty()).toBe(false)
+ expect(cursors[1].selection.isEmpty()).toBe(false)
+ expect(cursors[2].selection.isEmpty()).toBe(false)
+ expect(cursors[0].selection.getText()).toEqual('two')
+ expect(cursors[1].selection.getText()).toEqual('two')
+ expect(cursors[2].selection.getText()).toEqual('two')
+
+ simulateTabKeyEvent()
+ expect(cursors[0].getBufferPosition()).toEqual([0, 13])
+ expect(cursors[1].getBufferPosition()).toEqual([7, 18])
+ expect(cursors[2].getBufferPosition()).toEqual([12, 15])
+ expect(cursors[0].selection.isEmpty()).toBe(true)
+ expect(cursors[1].selection.isEmpty()).toBe(true)
+ expect(cursors[2].selection.isEmpty()).toBe(true)
+
+ simulateTabKeyEvent()
+ expect(cursors[0].getBufferPosition()).toEqual([0, 0])
+ expect(cursors[1].getBufferPosition()).toEqual([7, 5])
+ expect(cursors[2].getBufferPosition()).toEqual([12, 2])
+ expect(cursors[0].selection.isEmpty()).toBe(true)
+ expect(cursors[1].selection.isEmpty()).toBe(true)
+ expect(cursors[2].selection.isEmpty()).toBe(true)
+ })
+ })
+ })
+
+ describe('when the cursors do not share common snippet prefixes', () => {
+ it('inserts tabs as normal', () => {
+ editor.insertText('t9')
+ editor.setCursorBufferPosition([12, 2])
+ editor.insertText(' t8')
+ editor.addCursorAtBufferPosition([0, 2])
+ simulateTabKeyEvent()
+ expect(editor.lineTextForBufferRow(0)).toBe('t9 var quicksort = function () {')
+ expect(editor.lineTextForBufferRow(12)).toBe('}; t8 ')
+ })
+ })
+
+ 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, 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()
+ const configDirPath = temp.mkdirSync('atom-config-dir-')
+ spyOn(atom, 'getConfigDirPath').andReturn(configDirPath)
+ atom.workspace.open('atom://.atom/snippets')
+
+ waitsFor(() => atom.workspace.getActiveTextEditor() != null)
runs(() => {
- expect(atom.workspace.getActiveTextEditor().getURI()).toBe(path.join(configDirPath, 'snippets.cson'));
- });
- });
- });
+ 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);
+ 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]]);
- });
- });
+ 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", () => {
- let availableSnippetsView = null;
+ let availableSnippetsView = null
beforeEach(() => {
Snippets.add(__filename, {
- ".source.js": {
- "test": {
- prefix: "test",
- body: "${1:Test pass you will}, young "
+ '.source.js': {
+ test: {
+ prefix: 'test',
+ body: '${1:Test pass you will}, young '
},
- "challenge": {
- prefix: "chal",
- body: "$1: ${2:To pass this challenge}"
+ challenge: {
+ prefix: 'chal',
+ body: '$1: ${2:To pass this challenge}'
}
}
}
- );
+ )
- delete Snippets.availableSnippetsView;
+ delete Snippets.availableSnippetsView
- atom.commands.dispatch(editorElement, "snippets:available");
+ atom.commands.dispatch(editorElement, 'snippets:available')
- waitsFor(() => atom.workspace.getModalPanels().length === 1);
+ waitsFor(() => atom.workspace.getModalPanels().length === 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);
- });
- });
-});
+ availableSnippetsView = atom.workspace.getModalPanels()[0].getItem()
+ })
+ })
+
+ it('renders a select list of all available snippets', () => {
+ expect(availableSnippetsView.selectListView.getSelectedItem().prefix).toBe('test')
+ expect(availableSnippetsView.selectListView.getSelectedItem().name).toBe('test')
+ expect(availableSnippetsView.selectListView.getSelectedItem().bodyText).toBe('${1:Test pass you will}, young ')
+
+ availableSnippetsView.selectListView.selectNext()
+
+ expect(availableSnippetsView.selectListView.getSelectedItem().prefix).toBe('chal')
+ expect(availableSnippetsView.selectListView.getSelectedItem().name).toBe('challenge')
+ expect(availableSnippetsView.selectListView.getSelectedItem().bodyText).toBe('$1: ${2:To pass this challenge}')
+ })
+
+ it('writes the selected snippet to the editor as snippet', () => {
+ availableSnippetsView.selectListView.confirmSelection()
+
+ expect(editor.getCursorScreenPosition()).toEqual([0, 18])
+ expect(editor.getSelectedText()).toBe('Test pass you will')
+ expect(editor.lineTextForBufferRow(0)).toBe('Test pass you will, young var quicksort = function () {')
+ })
+
+ it('closes the dialog when triggered again', () => {
+ atom.commands.dispatch(availableSnippetsView.selectListView.refs.queryEditor.element, 'snippets:available')
+ expect(atom.workspace.getModalPanels().length).toBe(0)
+ })
+ })
+})
diff --git a/specs/snippets-spec.js b/specs/snippets-spec.js
new file mode 100644
index 00000000..e85411ea
--- /dev/null
+++ b/specs/snippets-spec.js
@@ -0,0 +1,64 @@
+const mock = require('mock-fs');
+
+const Snippets = require('../snippets')
+
+describe('Snippets', () => {
+ // userSnippetsPath
+ // loadSnippetsFile()
+ // loadUserSnippets()
+ // loadPackage()
+ // unloadPackage()
+ //
+ // activate
+ // deactivate
+
+ // snippets
+ describe('snippets API', () => {
+ const api = Snippets.snippets()
+ /*
+ userSnippetsPath: () => this.userSnippetsPath,
+ snippetsByScopes: () => this.snippetsByScopes,
+ */
+
+ describe('userSnippetsPath', () => {
+ waitsForPromise(() => atom.workspace.open('sample.js'))
+
+ const editor = atom.workspace.getActiveTextEditor()
+
+ it('returns a `Snippet`', () => {
+ const snippet = api.parse('this is a snippet')
+
+ expect(snippet).toBeInstanceOf(Snippet)
+ })
+ })
+
+ describe('loaded', () => {
+ describe('before activation', () => {
+ it('should resolve to false', () => {
+ expect(api.loaded).toBeInstanceOf(Promise)
+ waitsForPromise(() => api.loaded.then(result => expect(result).toBe(false)))
+ })
+ })
+
+ waitsForPromise(() => atom.packages.activatePackage('snippets-dev'))
+
+ describe('after activation', () => {
+ it('should resolve to true', () => {
+ expect(api.loaded).toBeInstanceOf(Promise)
+ waitsForPromise(() => api.loaded.then(result => expect(result).toBe(true)))
+ })
+ })
+
+ waitsForPromise(() => atom.packages.deactivatePackage('snippets-dev'))
+
+ describe('after deactivation', () => {
+ it('should resolve to false', () => {
+ expect(api.loaded).toBeInstanceOf(Promise)
+ waitsForPromise(() => api.loaded.then(result => expect(result).toBe(false)))
+ })
+ })
+ })
+
+ describe('')
+ })
+})