From 3f374237448e9d89a75095133bd27214b6390d13 Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Sun, 19 Jul 2020 01:14:44 +0300 Subject: [PATCH 01/17] package: install standard and define linting scripts The project mostly already followed standard style This'll help to catch any errors in new code and keep their style consistent And while we're at it remove now unused `coffeelint` --- .coffeelintignore | 1 - coffeelint.json | 37 - package-lock.json | 2075 ++++++++++++++++++++++++++++++++++++++++++--- package.json | 22 +- 4 files changed, 1976 insertions(+), 159 deletions(-) delete mode 100644 .coffeelintignore delete mode 100644 coffeelint.json 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/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/package-lock.json b/package-lock.json index c0850503..f9fcea78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,11 +4,119 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "dev": true + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, + "ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "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-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, + "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.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, + "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", @@ -42,11 +150,49 @@ "concat-map": "0.0.1" } }, + "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" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", @@ -67,43 +213,53 @@ "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": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, "cson-parser": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-1.3.5.tgz", @@ -120,11 +276,72 @@ "es5-ext": "~0.10.2" } }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "debug-log": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", + "dev": true + }, "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" + } + }, + "deglob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/deglob/-/deglob-4.0.1.tgz", + "integrity": "sha512-/g+RDZ7yf2HvoW+E5Cy+K94YhgcFgr6C8LuHZD1O5HoNPkf3KY6RfXJ0DBGlB/NkLi5gml+G9zqRzk9S0mHZCg==", + "dev": true, + "requires": { + "find-root": "^1.0.0", + "glob": "^7.0.5", + "ignore": "^5.0.0", + "pkg-config": "^1.1.0", + "run-parallel": "^1.1.2", + "uniq": "^1.0.1" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + } + } + }, + "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,6 +353,51 @@ "underscore-plus": "1.x" } }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "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.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "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", @@ -204,106 +466,904 @@ "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": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, "requires": { - "async": "^1.5.2", + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "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", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", "mkdirp": "^0.5.1", - "rimraf": "^2.5.2", - "underscore-plus": "1.x" + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "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-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 + }, + "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 + }, + "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" + } + }, + "strip-json-comments": { + "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 } } }, - "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": "14.1.1", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz", + "integrity": "sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg==", + "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": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-8.1.0.tgz", + "integrity": "sha512-ULVC8qH8qCqbU792ZOO6DaiaZyHNS/5CZt3hKqHkEhVlhPEPN3nfBqqxJCyp59XrjIBZPu1chMYe9T2DXZ7TMw==", + "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 + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } } }, - "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": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-2.0.0.tgz", + "integrity": "sha512-f6fceVtg27BR02EYnBhgWLFQfK6bN4Ll0nQFrBHOlCsAyxeZkn0NHns5O0YZOPrV1B3ramd6cgFwaoFLcSkwEQ==", + "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "eslint-utils": "^1.4.2", + "regexpp": "^3.0.0" + }, + "dependencies": { + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + } } }, - "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.18.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", + "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==", + "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "array-includes": "^3.0.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.11.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 + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } } }, - "key-path-helpers": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/key-path-helpers/-/key-path-helpers-0.1.0.tgz", - "integrity": "sha1-zYFJULeZzHRaNGqlIfkilK9du6Q=" - }, + "eslint-plugin-node": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-10.0.0.tgz", + "integrity": "sha512-1CSyM/QCjs6PXaT18+zuAXsjXGIGo5Rw630rSKwokSs2jrYURQc4R5JZpoanNCqwNmepg+0eZ9L7YiRUJb8jiQ==", + "dev": true, + "requires": { + "eslint-plugin-es": "^2.0.0", + "eslint-utils": "^1.4.2", + "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 + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "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.14.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz", + "integrity": "sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.1.0", + "object.entries": "^1.1.0", + "object.fromentries": "^2.0.0", + "object.values": "^1.1.0", + "prop-types": "^15.7.2", + "resolve": "^1.10.1" + }, + "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" + } + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-plugin-standard": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", + "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "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": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.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.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", + "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "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.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" + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "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 + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "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-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "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" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "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" + } + }, + "rimraf": { + "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.1.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.0.1", + "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-3.0.1.tgz", + "integrity": "sha1-VMFpxA4ohKZtNSeA0Y3TH5HToQ0=", + "requires": { + "async": "^1.5.2", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.2", + "underscore-plus": "1.x" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + } + } + }, + "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-stdin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", + "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "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.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "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" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "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-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.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "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.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "inquirer": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.2.tgz", + "integrity": "sha512-DF4osh1FM6l0RJc5YWYhSDB6TawiBRlbV9Cox8MWlidU218Tb7fm3lQTULyUJDfJ0tjbzl0W4q651mrCCEM55w==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.16", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "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": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "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 + }, + "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 + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "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": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "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-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "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": "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" + } + }, + "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-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "dev": true, + "requires": { + "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.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "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": "2.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", + "integrity": "sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "object.assign": "^4.1.0" + } + }, + "key-path-helpers": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/key-path-helpers/-/key-path-helpers-0.1.0.tgz", + "integrity": "sha1-zYFJULeZzHRaNGqlIfkilK9du6Q=" + }, "lcid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", @@ -312,15 +1372,68 @@ "invert-kv": "^1.0.0" } }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "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.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "dev": true + }, "loophole": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/loophole/-/loophole-1.1.0.tgz", "integrity": "sha1-N5Sf6kU7YlasxyXDIM4MWn9wor0=" }, + "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" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, "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" } @@ -343,11 +1456,129 @@ "minimist": "0.0.8" } }, + "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 + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "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 + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "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": { + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "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.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "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.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.entries": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz", + "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "has": "^1.0.3" + } + }, + "object.fromentries": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz", + "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -356,6 +1587,29 @@ "wrappy": "1" } }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, "os-locale": { "version": "1.4.0", "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", @@ -369,16 +1623,224 @@ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, + "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": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "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 + }, + "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": { + "pify": "^2.0.0" + } + }, "pegjs": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.8.0.tgz", "integrity": "sha1-l28GfaE+XFsVAcAXklZoolOBFWE=" }, + "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": { + "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 + } + } + }, + "pkg-config": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", + "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", + "dev": true, + "requires": { + "debug-log": "^1.0.0", + "find-root": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "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": { + "find-up": "^2.1.0" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "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", "resolved": "https://registry.npmjs.org/property-accessors/-/property-accessors-1.1.3.tgz", @@ -388,12 +1850,61 @@ "mixto": "1.x" } }, - "resolve": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz", - "integrity": "sha1-3ZV5gufnNt699TtYpN2RdUV13UY=", + "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" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "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 }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", @@ -402,6 +1913,33 @@ "glob": "^7.0.5" } }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "rxjs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", + "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, "scoped-property-store": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/scoped-property-store/-/scoped-property-store-0.17.0.tgz", @@ -424,6 +1962,127 @@ "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": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "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" + }, + "dependencies": { + "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 + } + } + }, + "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.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "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": "14.3.4", + "resolved": "https://registry.npmjs.org/standard/-/standard-14.3.4.tgz", + "integrity": "sha512-+lpOkFssMkljJ6eaILmqxHQ2n4csuEABmcubLTb9almFi1ElDzXb1819fjf/5ygSyePCq4kU2wMdb2fBfb9P9Q==", + "dev": true, + "requires": { + "eslint": "~6.8.0", + "eslint-config-standard": "14.1.1", + "eslint-config-standard-jsx": "8.1.0", + "eslint-plugin-import": "~2.18.0", + "eslint-plugin-node": "~10.0.0", + "eslint-plugin-promise": "~4.2.1", + "eslint-plugin-react": "~7.14.2", + "eslint-plugin-standard": "~4.0.0", + "standard-engine": "^12.0.0" + } + }, + "standard-engine": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-12.1.0.tgz", + "integrity": "sha512-DVJnWM1CGkag4ucFLGdiYWa5/kJURPONmMmk17p8FT5NE4UnPZB1vxWnXnRo2sPSL78pWJG8xEM+1Tu19z0deg==", + "dev": true, + "requires": { + "deglob": "^4.0.1", + "get-stdin": "^7.0.0", + "minimist": "^1.2.5", + "pkg-conf": "^3.1.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + } + } + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -434,6 +2093,26 @@ "strip-ansi": "^3.0.0" } }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "strip-ansi": { "version": "3.0.1", "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -442,12 +2121,73 @@ "ansi-regex": "^2.0.0" } }, - "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=", + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "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" + }, + "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 + }, + "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 + }, + "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 + }, + "string-width": { + "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" + } + }, + "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" + } + } + } + }, "temp": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", @@ -464,6 +2204,48 @@ } } }, + "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 + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "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 + }, "underscore": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", @@ -477,15 +2259,55 @@ "underscore": "~1.6.0" } }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "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": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "window-size": { "version": "0.1.4", "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": { @@ -502,6 +2324,21 @@ "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" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, "y18n": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", diff --git a/package.json b/package.json index 7ced0272..a4e797f8 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,11 @@ "engines": { "atom": "*" }, + "scripts": { + "lint": "standard", + "lint:fix": "npm run lint -- --fix", + "test": "npm run lint:fix && atom --test spec" + }, "dependencies": { "async": "~0.2.6", "atom-select-list": "^0.7.0", @@ -19,6 +24,9 @@ "temp": "~0.8.0", "underscore-plus": "^1.0.0" }, + "devDependencies": { + "standard": "^14.3.4" + }, "providedServices": { "snippets": { "description": "Snippets are text shortcuts that can be expanded to their definition.", @@ -27,7 +35,17 @@ } } }, - "devDependencies": { - "coffeelint": "^1.9.7" + "standard": { + "env": { + "jasmine": true, + "node": true + }, + "ignore": [ + "spec/fixtures/" + ], + "globals": [ + "waitsForPromise", + "atom" + ] } } From 67048df1857830dfd3bc5795de928bc144c44810 Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Sun, 19 Jul 2020 01:22:35 +0300 Subject: [PATCH 02/17] package: update deps and reorder package.json Fixes a few vulnerabilities reported by npm The reorder isn't necessary, I just thinks it looks better --- package-lock.json | 98 +++++++++++++++++++++++++++++++---------------- package.json | 16 ++++---- 2 files changed, 73 insertions(+), 41 deletions(-) diff --git a/package-lock.json b/package-lock.json index f9fcea78..e82984e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -118,9 +118,9 @@ "dev": true }, "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" }, "atom-select-list": { "version": "0.7.2", @@ -972,9 +972,9 @@ "dev": true }, "fs-plus": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-3.0.1.tgz", - "integrity": "sha1-VMFpxA4ohKZtNSeA0Y3TH5HToQ0=", + "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", @@ -1021,6 +1021,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1439,9 +1440,9 @@ } }, "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "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", @@ -1449,11 +1450,11 @@ "integrity": "sha1-wyDvYbUvKJj1IuF9i7xtUG2EJbY=" }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "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": "0.0.8" + "minimist": "^1.2.5" } }, "ms": { @@ -1621,7 +1622,8 @@ "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=" + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true }, "p-limit": { "version": "1.3.0", @@ -1906,11 +1908,26 @@ } }, "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "requires": { - "glob": "^7.0.5" + "glob": "^7.1.3" + }, + "dependencies": { + "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" + } + } } }, "run-async": { @@ -2189,18 +2206,33 @@ } }, "temp": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", - "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.1.tgz", + "integrity": "sha512-WMuOgiua1xb5R56lE0eH6ivpVmg/lq2OHm4+LtT/xtEtPQ+sz6N3bBM6WZ5FvO1lO4IKIOb43qnhoc4qxP5OeA==", "requires": { - "os-tmpdir": "^1.0.0", - "rimraf": "~2.2.6" + "rimraf": "~2.6.2" }, "dependencies": { + "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" + } + }, "rimraf": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } } } }, @@ -2247,16 +2279,16 @@ "dev": true }, "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", + "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==" }, "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.6.0" + "underscore": "^1.9.1" } }, "uniq": { diff --git a/package.json b/package.json index a4e797f8..af7fac79 100644 --- a/package.json +++ b/package.json @@ -1,32 +1,32 @@ { "name": "snippets", "version": "1.6.0", - "main": "./lib/snippets", "description": "Expand snippets matching the current prefix with `tab`.", "repository": "https://github.com/atom/snippets", "license": "MIT", - "engines": { - "atom": "*" - }, + "main": "./lib/snippets", "scripts": { "lint": "standard", "lint:fix": "npm run lint -- --fix", "test": "npm run lint:fix && atom --test spec" }, "dependencies": { - "async": "~0.2.6", + "async": "^3.2.0", "atom-select-list": "^0.7.0", - "fs-plus": "^3.0.0", + "fs-plus": "^3.1.1", "loophole": "^1", "pegjs": "~0.8.0", "scoped-property-store": "^0.17.0", "season": "^6.0.2", - "temp": "~0.8.0", - "underscore-plus": "^1.0.0" + "temp": "^0.9.1", + "underscore-plus": "^1.7.0" }, "devDependencies": { "standard": "^14.3.4" }, + "engines": { + "atom": "*" + }, "providedServices": { "snippets": { "description": "Snippets are text shortcuts that can be expanded to their definition.", From 6e024467d7c26e172410801170bb116abe60359d Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Sun, 19 Jul 2020 01:35:35 +0300 Subject: [PATCH 03/17] package: run lint:fix Most of the changes we're made by `standard` automatically --- lib/helpers.js | 4 +- lib/insertion.js | 8 +- lib/snippet-body-parser.js | 4 +- lib/snippet-expansion.js | 26 +- lib/snippet-history-provider.js | 2 +- lib/snippet.js | 10 +- lib/snippets-available.js | 6 +- lib/snippets.js | 89 +- lib/tab-stop-list.js | 8 +- lib/tab-stop.js | 18 +- spec/body-parser-spec.js | 203 ++-- spec/insertion-spec.js | 20 +- spec/snippet-loading-spec.js | 406 +++---- spec/snippets-spec.js | 2018 ++++++++++++++++--------------- 14 files changed, 1415 insertions(+), 1407 deletions(-) diff --git a/lib/helpers.js b/lib/helpers.js index 0814a3df..461e7747 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -2,8 +2,8 @@ import path from 'path' -export function getPackageRoot() { - const {resourcePath} = atom.getLoadSettings() +export function getPackageRoot () { + const { resourcePath } = atom.getLoadSettings() const currentFileWasRequiredFromSnapshot = !path.isAbsolute(__dirname) if (currentFileWasRequiredFromSnapshot) { return path.join(resourcePath, 'node_modules', 'snippets') diff --git a/lib/insertion.js b/lib/insertion.js index 96065d1e..874fa0bf 100644 --- a/lib/insertion.js +++ b/lib/insertion.js @@ -62,21 +62,21 @@ class Insertion { makeReplacer (replace) { return function replacer (...match) { - let flags = { + const flags = { uppercaseAll: false, lowercaseAll: false, uppercaseNext: false, lowercaseNext: false } replace = [...replace] - let result = [] + const 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) + const transformed = transformText(match[token.backreference], flags) result.push(transformed) } }) @@ -85,7 +85,7 @@ class Insertion { } transform (input) { - let { substitution } = this + const { substitution } = this if (!substitution) { return input } return input.replace(substitution.find, this.replacer) } diff --git a/lib/snippet-body-parser.js b/lib/snippet-body-parser.js index d4293ecb..5fdcdab6 100644 --- a/lib/snippet-body-parser.js +++ b/lib/snippet-body-parser.js @@ -2,13 +2,13 @@ let parser try { parser = require('./snippet-body') } catch (error) { - const {allowUnsafeEval} = require('loophole') + 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)) + allowUnsafeEval(() => { parser = PEG.buildParser(grammarSrc) }) } module.exports = parser diff --git a/lib/snippet-expansion.js b/lib/snippet-expansion.js index 54a525bc..00cfbc56 100644 --- a/lib/snippet-expansion.js +++ b/lib/snippet-expansion.js @@ -1,7 +1,7 @@ -const {CompositeDisposable, Range, Point} = require('atom') +const { CompositeDisposable, Range, Point } = require('atom') module.exports = class SnippetExpansion { - constructor(snippet, editor, cursor, snippets) { + constructor (snippet, editor, cursor, snippets) { this.settingTabStop = false this.isIgnoringBufferChanges = false this.onUndoOrRedo = this.onUndoOrRedo.bind(this) @@ -9,15 +9,15 @@ module.exports = class SnippetExpansion { this.editor = editor this.cursor = cursor this.snippets = snippets - this.subscriptions = new CompositeDisposable + this.subscriptions = new CompositeDisposable() this.tabStopMarkers = [] this.selections = [this.cursor.selection] const startPosition = this.cursor.selection.getBufferRange().start - let {body, tabStopList} = this.snippet + let { body, tabStopList } = this.snippet let tabStops = tabStopList.toArray() - let indent = this.editor.lineTextForBufferRow(startPosition.row).match(/^\s*/)[0] + const 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}`) @@ -28,7 +28,7 @@ module.exports = class SnippetExpansion { this.editor.transact(() => { this.ignoringBufferChanges(() => { this.editor.transact(() => { - const newRange = this.cursor.selection.insertText(body, {autoIndent: false}) + 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())) @@ -47,7 +47,7 @@ module.exports = class SnippetExpansion { this.isUndoingOrRedoing = true } - cursorMoved ({oldBufferPosition, newBufferPosition, textChanged}) { + cursorMoved ({ oldBufferPosition, newBufferPosition, textChanged }) { if (this.settingTabStop || textChanged) { return } const itemWithCursor = this.tabStopMarkers[this.tabStopIndex].find(item => item.marker.getBufferRange().containsPoint(newBufferPosition)) @@ -95,7 +95,7 @@ module.exports = class SnippetExpansion { this.ignoringBufferChanges(() => { for (const item of items) { - const {marker, insertion} = item + const { marker, insertion } = item var range = marker.getBufferRange() // Don't transform mirrored tab stops. They have their own cursors, so @@ -115,14 +115,14 @@ module.exports = class SnippetExpansion { placeTabStopMarkers (startPosition, tabStops) { for (const tabStop of tabStops) { - const {insertions} = tabStop + const { insertions } = tabStop const markers = [] if (!tabStop.isValid()) { continue } for (const insertion of insertions) { - const {range} = insertion - const {start, end} = range + const { range } = insertion + const { start, end } = range const marker = this.getMarkerLayer(this.editor).markBufferRange([ startPosition.traverse(start), startPosition.traverse(end) @@ -178,7 +178,7 @@ module.exports = class SnippetExpansion { const ranges = [] this.hasTransforms = false for (const item of items) { - const {marker, insertion} = item + const { marker, insertion } = item if (marker.isDestroyed()) { continue } if (!marker.isValid()) { continue } if (insertion.isTransformation()) { @@ -217,7 +217,7 @@ module.exports = class SnippetExpansion { 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] + const { marker: lastMarker } = items[items.length - 1] if (lastMarker.isDestroyed()) { return false } else { diff --git a/lib/snippet-history-provider.js b/lib/snippet-history-provider.js index b1b3e57c..0c78f3f0 100644 --- a/lib/snippet-history-provider.js +++ b/lib/snippet-history-provider.js @@ -1,5 +1,5 @@ function wrap (manager, callbacks) { - let klass = new SnippetHistoryProvider(manager) + const klass = new SnippetHistoryProvider(manager) return new Proxy(manager, { get (target, name) { if (name in callbacks) { diff --git a/lib/snippet.js b/lib/snippet.js index fcdfed90..8ff6ce7e 100644 --- a/lib/snippet.js +++ b/lib/snippet.js @@ -1,8 +1,8 @@ -const {Range} = require('atom') +const { Range } = require('atom') const TabStopList = require('./tab-stop-list') module.exports = class Snippet { - constructor({name, prefix, bodyText, description, descriptionMoreURL, rightLabelHTML, leftLabel, leftLabelHTML, bodyTree}) { + constructor ({ name, prefix, bodyText, description, descriptionMoreURL, rightLabelHTML, leftLabel, leftLabelHTML, bodyTree }) { this.name = name this.prefix = prefix this.bodyText = bodyText @@ -21,11 +21,11 @@ module.exports = class Snippet { let column = 0 // recursive helper function; mutates vars above - let extractTabStops = bodyTree => { + const extractTabStops = bodyTree => { for (const segment of bodyTree) { if (segment.index != null) { - let {index, content, substitution} = segment - if (index === 0) { index = Infinity; } + let { index, content, substitution } = segment + if (index === 0) { index = Infinity } const start = [row, column] extractTabStops(content) const range = new Range(start, [row, column]) diff --git a/lib/snippets-available.js b/lib/snippets-available.js index d244cb16..7a760a00 100644 --- a/lib/snippets-available.js +++ b/lib/snippets-available.js @@ -70,15 +70,15 @@ export default class SnippetsAvailable { populate () { const snippets = Object.values(this.snippets.getSnippets(this.editor)) - for (let snippet of snippets) { + for (const snippet of snippets) { snippet.searchText = _.compact([snippet.prefix, snippet.name]).join(' ') } - return this.selectListView.update({items: snippets}) + return this.selectListView.update({ items: snippets }) } attach () { this.previouslyFocusedElement = document.activeElement - this.panel = atom.workspace.addModalPanel({item: this}) + this.panel = atom.workspace.addModalPanel({ item: this }) this.selectListView.focus() } } diff --git a/lib/snippets.js b/lib/snippets.js index 8e67ec0b..9f12becd 100644 --- a/lib/snippets.js +++ b/lib/snippets.js @@ -1,5 +1,5 @@ const path = require('path') -const {Emitter, Disposable, CompositeDisposable, File} = require('atom') +const { Emitter, Disposable, CompositeDisposable, File } = require('atom') const _ = require('underscore-plus') const async = require('async') const CSON = require('season') @@ -9,27 +9,27 @@ const ScopedPropertyStore = require('scoped-property-store') const Snippet = require('./snippet') const SnippetExpansion = require('./snippet-expansion') const EditorStore = require('./editor-store') -const {getPackageRoot} = require('./helpers') +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.snippetsByPackage = new Map() + this.parsedSnippetsById = new Map() + this.editorMarkerLayers = new WeakMap() - this.scopedPropertyStore = new ScopedPropertyStore + 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.disabledSnippetsScopedPropertyStore = new ScopedPropertyStore() - this.subscriptions = new CompositeDisposable + this.subscriptions = new CompositeDisposable() this.subscriptions.add(atom.workspace.addOpener(uri => { if (uri === 'atom://.atom/snippets') { return atom.workspace.openTextFile(this.getUserSnippetsPath()) @@ -41,14 +41,14 @@ module.exports = { this.subscriptions.add(watchDisposable) }) - this.subscriptions.add(atom.config.onDidChange('core.packagesWithSnippetsDisabled', ({newValue, oldValue}) => { - this.handleDisabledPackagesDidChange(newValue, oldValue) + 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) { + 'snippets:expand' (event) { const editor = this.getModel() if (snippets.snippetToExpandUnderCursor(editor)) { snippets.clearExpansions(editor) @@ -58,17 +58,17 @@ module.exports = { } }, - 'snippets:next-tab-stop'(event) { + 'snippets:next-tab-stop' (event) { const editor = this.getModel() if (!snippets.goToNextTabStop(editor)) { event.abortKeyBinding() } }, - 'snippets:previous-tab-stop'(event) { + 'snippets:previous-tab-stop' (event) { const editor = this.getModel() if (!snippets.goToPreviousTabStop(editor)) { event.abortKeyBinding() } }, - 'snippets:available'(event) { + 'snippets:available' (event) { const editor = this.getModel() const SnippetsAvailable = require('./snippets-available') if (snippets.availableSnippetsView == null) { snippets.availableSnippetsView = new SnippetsAvailable(snippets) } @@ -96,8 +96,8 @@ module.exports = { loadAll () { this.loadBundledSnippets(bundledSnippets => { - this.loadPackageSnippets(packageSnippets => { - this.loadUserSnippets(userSnippets => { + this.loadPackageSnippets((packageSnippets = {}) => { + this.loadUserSnippets((userSnippets = {}) => { atom.config.transact(() => { for (const snippetSet of [bundledSnippets, packageSnippets, userSnippets]) { for (const filepath in snippetSet) { @@ -114,7 +114,7 @@ module.exports = { loadBundledSnippets (callback) { const bundledSnippetsPath = CSON.resolve(path.join(getPackageRoot(), 'lib', 'snippets')) - this.loadSnippetsFile(bundledSnippetsPath, snippets => { + this.loadSnippetsFile(bundledSnippetsPath, (snippets = {}) => { const snippetsByPath = {} snippetsByPath[bundledSnippetsPath] = snippets callback(snippetsByPath) @@ -124,14 +124,14 @@ module.exports = { loadUserSnippets (callback) { const userSnippetsPath = this.getUserSnippetsPath() fs.stat(userSnippetsPath, (error, stat) => { - if (stat != null && stat.isFile()) { - this.loadSnippetsFile(userSnippetsPath, snippets => { + if (!error && stat.isFile()) { + this.loadSnippetsFile(userSnippetsPath, (snippets = {}) => { const result = {} result[userSnippetsPath] = snippets callback(result) }) } else { - callback({}) + callback() } }) }, @@ -139,7 +139,7 @@ module.exports = { watchUserSnippets (callback) { const userSnippetsPath = this.getUserSnippetsPath() fs.stat(userSnippetsPath, (error, stat) => { - if (stat != null && stat.isFile()) { + if (!error && stat.isFile()) { const userSnippetsFileDisposable = new CompositeDisposable() const userSnippetsFile = new File(userSnippetsPath) try { @@ -155,7 +155,7 @@ module.exports = { [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}) + atom.notifications.addError(message, { dismissable: true }) } callback(userSnippetsFileDisposable) @@ -171,7 +171,7 @@ module.exports = { const userSnippetsPath = this.getUserSnippetsPath() atom.config.transact(() => { this.clearSnippetsForPath(userSnippetsPath) - this.loadSnippetsFile(userSnippetsPath, result => { + this.loadSnippetsFile(userSnippetsPath, (result = {}) => { this.add(userSnippetsPath, result) }) }) @@ -227,9 +227,12 @@ module.exports = { } async.map(snippetsDirPaths, this.loadSnippetsDirectory.bind(this), (error, results) => { + if (error) { + callback() + } const zipped = [] for (const key in results) { - zipped.push({result: results[key], pack: packages[key]}) + zipped.push({ result: results[key], pack: packages[key] }) } const enabledPackages = [] @@ -267,7 +270,7 @@ module.exports = { getEmitter () { if (this.emitter == null) { - this.emitter = new Emitter + this.emitter = new Emitter() } return this.emitter }, @@ -286,25 +289,25 @@ module.exports = { entries, (entry, done) => { const filePath = path.join(snippetsDirPath, entry) - this.loadSnippetsFile(filePath, snippets => done(null, {filePath, snippets})) + this.loadSnippetsFile(filePath, (snippets = {}) => done(null, { filePath, snippets })) }, (error, results) => { const snippetsByPath = {} - for (const {filePath, snippets} of results) { + for (const { filePath, snippets } of results) { snippetsByPath[filePath] = snippets } - callback(null, snippetsByPath) - }) + callback(error, snippetsByPath) + }) }) }) }, loadSnippetsFile (filePath, callback) { - if (!CSON.isObjectPath(filePath)) { return callback({}) } - CSON.readFile(filePath, {allowDuplicateKeys: false}, (error, object = {}) => { + 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}) + atom.notifications.addError(`Failed to load snippets from '${filePath}'`, { detail: error.message, dismissable: true }) } callback(object) }) @@ -316,7 +319,7 @@ module.exports = { const unparsedSnippetsByPrefix = {} for (const name in snippetsByName) { const attributes = snippetsByName[name] - const {prefix, body} = attributes + const { prefix, body } = attributes attributes.name = name attributes.id = this.snippetIdCounter++ if (typeof body === 'string') { @@ -355,9 +358,9 @@ module.exports = { // 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} + unparsedSnippets[selector] = { snippets: value } const store = isDisabled ? this.disabledSnippetsScopedPropertyStore : this.scopedPropertyStore - store.addProperties(path, unparsedSnippets, {priority: this.priorityForSource(path)}) + store.addProperties(path, unparsedSnippets, { priority: this.priorityForSource(path) }) }, clearSnippetsForPath (path) { @@ -377,7 +380,7 @@ module.exports = { const unparsedSnippetsByPrefix = this.scopedPropertyStore.getPropertyValue( this.getScopeChain(scopeDescriptor), - "snippets" + 'snippets' ) const legacyScopeDescriptor = atom.config.getLegacyScopeDescriptorForNewScopeDescriptor @@ -387,7 +390,7 @@ module.exports = { if (legacyScopeDescriptor) { unparsedLegacySnippetsByPrefix = this.scopedPropertyStore.getPropertyValue( this.getScopeChain(legacyScopeDescriptor), - "snippets" + 'snippets' ) } @@ -416,9 +419,9 @@ module.exports = { getParsedSnippet (attributes) { let snippet = this.parsedSnippetsById.get(attributes.id) if (snippet == null) { - let {id, prefix, name, body, bodyTree, description, descriptionMoreURL, rightLabelHTML, leftLabel, leftLabelHTML} = attributes + 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}) + snippet = new Snippet({ id, name, prefix, bodyTree, description, descriptionMoreURL, rightLabelHTML, leftLabel, leftLabelHTML, bodyText: body }) this.parsedSnippetsById.set(attributes.id, snippet) } return snippet @@ -453,7 +456,7 @@ module.exports = { for (const cursor of editor.getCursors()) { const position = cursor.getBufferPosition() - const prefixStart = cursor.getBeginningOfCurrentWordBufferPosition({wordRegex}) + const prefixStart = cursor.getBeginningOfCurrentWordBufferPosition({ wordRegex }) const cursorSnippetPrefix = editor.getTextInRange([prefixStart, position]) if ((snippetPrefix != null) && (cursorSnippetPrefix !== snippetPrefix)) { return null } snippetPrefix = cursorSnippetPrefix @@ -464,7 +467,7 @@ module.exports = { wordPrefix = cursorWordPrefix } - return {snippetPrefix, wordPrefix} + return { snippetPrefix, wordPrefix } }, // Get a RegExp of all the characters used in the snippet prefixes @@ -560,7 +563,7 @@ module.exports = { findOrCreateMarkerLayer (editor) { let layer = this.editorMarkerLayers.get(editor) if (layer === undefined) { - layer = editor.addMarkerLayer({maintainHistory: true}) + layer = editor.addMarkerLayer({ maintainHistory: true }) this.editorMarkerLayers.set(editor, layer) } return layer @@ -624,7 +627,7 @@ module.exports = { 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}) + snippet = new Snippet({ name: '__anonymous', prefix: '', bodyTree, bodyText: snippet }) } return new SnippetExpansion(snippet, editor, cursor, this) }, diff --git a/lib/tab-stop-list.js b/lib/tab-stop-list.js index 0d3bd010..7391b243 100644 --- a/lib/tab-stop-list.js +++ b/lib/tab-stop-list.js @@ -22,12 +22,12 @@ class TabStopList { } forEachIndex (iterator) { - let indices = Object.keys(this.list).sort((a1, a2) => a1 - a2) + const indices = Object.keys(this.list).sort((a1, a2) => a1 - a2) indices.forEach(iterator) } getInsertions () { - let results = [] + const results = [] this.forEachIndex(index => { results.push(...this.list[index].insertions) }) @@ -35,9 +35,9 @@ class TabStopList { } toArray () { - let results = [] + const results = [] this.forEachIndex(index => { - let tabStop = this.list[index] + const tabStop = this.list[index] if (!tabStop.isValid()) return results.push(tabStop) }) diff --git a/lib/tab-stop.js b/lib/tab-stop.js index 61a423e4..e6af3403 100644 --- a/lib/tab-stop.js +++ b/lib/tab-stop.js @@ -1,4 +1,4 @@ -const {Range} = require('atom') +const { Range } = require('atom') const Insertion = require('./insertion') // A tab stop: @@ -12,22 +12,22 @@ class TabStop { } isValid () { - let any = this.insertions.some(insertion => insertion.isTransformation()) + const any = this.insertions.some(insertion => insertion.isTransformation()) if (!any) return true - let all = this.insertions.every(insertion => insertion.isTransformation()) + const 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 }) + const 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()) + const initial = insertions.find(insertion => !insertion.isTransformation()) if (initial) { insertions.splice(insertions.indexOf(initial), 1) insertions.unshift(initial) @@ -36,10 +36,10 @@ class TabStop { } copyWithIndent (indent) { - let { snippet, index, insertions } = this - let newInsertions = insertions.map(insertion => { - let { range, substitution } = insertion - let newRange = Range.fromObject(range, true) + const { snippet, index, insertions } = this + const newInsertions = insertions.map(insertion => { + const { range, substitution } = insertion + const newRange = Range.fromObject(range, true) if (newRange.start.row) { newRange.start.column += indent.length newRange.end.column += indent.length diff --git a/spec/body-parser-spec.js b/spec/body-parser-spec.js index 35492ded..7af6365e 100644 --- a/spec/body-parser-spec.js +++ b/spec/body-parser-spec.js @@ -1,86 +1,88 @@ -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 BodyParser = require('../lib/snippet-body-parser') + +describe('Snippet Body Parser', () => { + it('breaks a snippet body into lines, with each line containing tab stops at the appropriate position', () => { const bodyTree = BodyParser.parse(`\ the quick brown $1fox \${2:jumped \${3:over} }the \${4:lazy} dog\ ` - ); + ) expect(bodyTree).toEqual([ - "the quick brown ", - {index: 1, content: []}, - "fox ", + 'the quick brown ', + { index: 1, content: [] }, + 'fox ', { index: 2, content: [ - "jumped ", - {index: 3, content: ["over"]}, - "\n" - ], + 'jumped ', + { index: 3, content: ['over'] }, + '\n' + ] }, - "the ", - {index: 4, content: ["lazy"]}, - " dog" - ]); - }); + 'the ', + { index: 4, content: ['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 = 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 = 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 = 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"); + it('parses a snippet with transformations', () => { + const bodyTree = BodyParser.parse('<${1:p}>$0') expect(bodyTree).toEqual([ '<', - {index: 1, content: ['p']}, + { index: 1, content: ['p'] }, '>', - {index: 0, content: []}, + { index: 0, content: [] }, '' - ]); - }); + ]) + }) - 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 = 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 +90,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 +106,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 = 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 +127,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"); + it('parses a snippet with a format string and case-control flags', () => { + const bodyTree = BodyParser.parse('<${1:p}>$0') expect(bodyTree).toEqual([ '<', - {index: 1, content: ['p']}, + { index: 1, content: ['p'] }, '>', - {index: 0, content: []}, + { index: 0, content: [] }, '' - ]); - }); + ]) + }) - 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"); + const bodyTree = BodyParser.parse('<${1:p}>$0') expect(bodyTree).toEqual([ '<', - {index: 1, content: ['p']}, + { index: 1, content: ['p'] }, '>', - {index: 0, content: []}, + { index: 0, content: [] }, '' - ]); - }); + ]) + }) it("parses a snippet with a placeholder that mirrors another tab stop's content", () => { - const bodyTree = BodyParser.parse("$4console.${3:log}('${2:$1}', $1);$0"); + const bodyTree = BodyParser.parse("$4console.${3:log}('${2:$1}', $1);$0") expect(bodyTree).toEqual([ - {index: 4, content: []}, + { 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 = 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..0207cebf 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,974 @@ 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(); + it('parses snippets once, reusing cached ones on subsequent queries', () => { + spyOn(Snippets, 'getBodyParser').andCallThrough() - editor.insertText("t1"); - simulateTabKeyEvent(); + 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]); + expect(Snippets.getBodyParser).toHaveBeenCalled() + expect(editor.lineTextForBufferRow(0)).toBe('this is a testvar quicksort = function () {') + expect(editor.getCursorScreenPosition()).toEqual([0, 14]) - Snippets.getBodyParser.reset(); + Snippets.getBodyParser.reset() - editor.setText(""); - editor.insertText("t1"); - simulateTabKeyEvent(); + editor.setText('') + editor.insertText('t1') + simulateTabKeyEvent() - expect(Snippets.getBodyParser).not.toHaveBeenCalled(); - expect(editor.lineTextForBufferRow(0)).toBe("this is a test"); - expect(editor.getCursorScreenPosition()).toEqual([0, 14]); + expect(Snippets.getBodyParser).not.toHaveBeenCalled() + expect(editor.lineTextForBufferRow(0)).toBe('this is a test') + expect(editor.getCursorScreenPosition()).toEqual([0, 14]) - Snippets.getBodyParser.reset(); + Snippets.getBodyParser.reset() 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(Snippets.getBodyParser).toHaveBeenCalled() + expect(editor.lineTextForBufferRow(0)).toBe('new snippet') + expect(editor.getCursorScreenPosition()).toEqual([0, 11]) + }) + + describe('when the snippet body is invalid or missing', () => { + it('does not register the snippet', () => { + editor.setText('') + editor.insertText('bad1') + atom.commands.dispatch(editorElement, 'snippets:expand') + expect(editor.getText()).toBe('bad1') + + editor.setText('') + editor.setText('bad2') + atom.commands.dispatch(editorElement, 'snippets:expand') + expect(editor.getText()).toBe('bad2') + }) + }) + + describe('when the letters preceding the cursor trigger a snippet', () => { + describe('when the snippet contains no tab stops', () => { + it('replaces the prefix with the snippet text and places the cursor at its end', () => { + editor.insertText('t1') + expect(editor.getCursorScreenPosition()).toEqual([0, 2]) + + simulateTabKeyEvent() + expect(editor.lineTextForBufferRow(0)).toBe('this is a testvar quicksort = function () {') + expect(editor.getCursorScreenPosition()).toEqual([0, 14]) + }) + + it('inserts a real tab the next time a tab is pressed after the snippet is expanded', () => { + editor.insertText('t1') + simulateTabKeyEvent() + expect(editor.lineTextForBufferRow(0)).toBe('this is a testvar quicksort = function () {') + simulateTabKeyEvent() + expect(editor.lineTextForBufferRow(0)).toBe('this is a test var quicksort = function () {') + }) + }) + + describe('when the snippet contains tab stops', () => { it("places the cursor at the first tab-stop, and moves the cursor in response to 'next-tab-stop' events", () => { - 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) + }) + }) +}) From f4786bdf35d30e78c28513777802fbee78d24d8f Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Sun, 19 Jul 2020 01:51:00 +0300 Subject: [PATCH 04/17] package: move `temp` to `devDependencies` It's only used by the specs --- package-lock.json | 3 +++ package.json | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index e82984e1..e04791bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2209,6 +2209,7 @@ "version": "0.9.1", "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.1.tgz", "integrity": "sha512-WMuOgiua1xb5R56lE0eH6ivpVmg/lq2OHm4+LtT/xtEtPQ+sz6N3bBM6WZ5FvO1lO4IKIOb43qnhoc4qxP5OeA==", + "dev": true, "requires": { "rimraf": "~2.6.2" }, @@ -2217,6 +2218,7 @@ "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2230,6 +2232,7 @@ "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.1.3" } diff --git a/package.json b/package.json index af7fac79..a1d0af31 100644 --- a/package.json +++ b/package.json @@ -18,11 +18,11 @@ "pegjs": "~0.8.0", "scoped-property-store": "^0.17.0", "season": "^6.0.2", - "temp": "^0.9.1", "underscore-plus": "^1.7.0" }, "devDependencies": { - "standard": "^14.3.4" + "standard": "^14.3.4", + "temp": "^0.9.1" }, "engines": { "atom": "*" From 318738a997938279f66df3811a5d369884b2b4ab Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Wed, 15 Jul 2020 08:41:07 +0300 Subject: [PATCH 05/17] snippet-body: follow suggested grammar style Based on https://github.com/pegjs/pegjs/issues/649 --- lib/snippet-body.pegjs | 125 +++++++++++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 42 deletions(-) diff --git a/lib/snippet-body.pegjs b/lib/snippet-body.pegjs index 476c65af..d0129025 100644 --- a/lib/snippet-body.pegjs +++ b/lib/snippet-body.pegjs @@ -21,62 +21,103 @@ }, []); } } + 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 +bodyContentChar + = escaped + / !tabStop char:. { return char; } -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 - }; -} +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); + } -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: [] }; -} +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 + } -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; } +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] }; -} +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; } -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) }; -}; +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 }; -} +formatStringEscape = "\\" flag:[ULulErn$] { return { escape: flag }; } From 36514b56bf454536ef86f259448898b2f9657299 Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Wed, 15 Jul 2020 09:15:51 +0300 Subject: [PATCH 06/17] package: update `pegjs` to latest @dev version Master has had a great number of improvements and bug fixes after the latest stable `0.10.0` version, which is over four years old by now. --- lib/snippet-body-parser.js | 4 ++-- package-lock.json | 6 +++--- package.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/snippet-body-parser.js b/lib/snippet-body-parser.js index 5fdcdab6..d8e35faa 100644 --- a/lib/snippet-body-parser.js +++ b/lib/snippet-body-parser.js @@ -4,11 +4,11 @@ try { } catch (error) { const { allowUnsafeEval } = require('loophole') const fs = require('fs-plus') - const PEG = require('pegjs') + const peg = require('pegjs') const grammarSrc = fs.readFileSync(require.resolve('./snippet-body.pegjs'), 'utf8') parser = null - allowUnsafeEval(() => { parser = PEG.buildParser(grammarSrc) }) + allowUnsafeEval(() => parser = peg.generate(grammarSrc)) } module.exports = parser diff --git a/package-lock.json b/package-lock.json index e04791bf..3dfb7dba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1700,9 +1700,9 @@ } }, "pegjs": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.8.0.tgz", - "integrity": "sha1-l28GfaE+XFsVAcAXklZoolOBFWE=" + "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==" }, "pify": { "version": "2.3.0", diff --git a/package.json b/package.json index a1d0af31..1f1c7720 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "atom-select-list": "^0.7.0", "fs-plus": "^3.1.1", "loophole": "^1", - "pegjs": "~0.8.0", + "pegjs": "^0.11.0-master.b7b87ea", "scoped-property-store": "^0.17.0", "season": "^6.0.2", "underscore-plus": "^1.7.0" From 832393bd947cf5e98854f4c0e57b29a89e954951 Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Thu, 16 Jul 2020 02:22:27 +0300 Subject: [PATCH 07/17] snippets: convert `getBodyParser` into an getter Simplifies the code and file structure significantly. Unfortunatelly we can't spy that the getter is invoked in specs, as the jasmin version included in atom is ancient. --- lib/snippet-body-parser.js | 14 ---------- ...t-body.pegjs => snippet-body-parser.pegjs} | 0 lib/snippets.js | 18 ++++++------- spec/body-parser-spec.js | 27 ++++++++++--------- spec/snippets-spec.js | 10 +------ 5 files changed, 25 insertions(+), 44 deletions(-) delete mode 100644 lib/snippet-body-parser.js rename lib/{snippet-body.pegjs => snippet-body-parser.pegjs} (100%) diff --git a/lib/snippet-body-parser.js b/lib/snippet-body-parser.js deleted file mode 100644 index d8e35faa..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.generate(grammarSrc)) -} - -module.exports = parser diff --git a/lib/snippet-body.pegjs b/lib/snippet-body-parser.pegjs similarity index 100% rename from lib/snippet-body.pegjs rename to lib/snippet-body-parser.pegjs diff --git a/lib/snippets.js b/lib/snippets.js index 9f12becd..404fceaa 100644 --- a/lib/snippets.js +++ b/lib/snippets.js @@ -4,6 +4,7 @@ const _ = require('underscore-plus') const async = require('async') const CSON = require('season') const fs = require('fs-plus') +const peg = require('pegjs') const ScopedPropertyStore = require('scoped-property-store') const Snippet = require('./snippet') @@ -86,6 +87,12 @@ module.exports = { atom.config.transact(() => this.subscriptions.dispose()) }, + get bodyParser () { + delete this.bodyParser + const grammar = fs.readFileSync(require.resolve('./snippet-body-parser.pegjs'), 'utf8') + return (this.bodyParser = peg.generate(grammar)) + }, + getUserSnippetsPath () { if (this.userSnippetsPath != null) { return this.userSnippetsPath } @@ -420,7 +427,7 @@ module.exports = { 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) } + if (bodyTree == null) { bodyTree = this.bodyParser.parse(body) } snippet = new Snippet({ id, name, prefix, bodyTree, description, descriptionMoreURL, rightLabelHTML, leftLabel, leftLabelHTML, bodyText: body }) this.parsedSnippetsById.set(attributes.id, snippet) } @@ -435,13 +442,6 @@ module.exports = { } }, - 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 @@ -626,7 +626,7 @@ module.exports = { if (editor == null) { editor = atom.workspace.getActiveTextEditor() } if (cursor == null) { cursor = editor.getLastCursor() } if (typeof snippet === 'string') { - const bodyTree = this.getBodyParser().parse(snippet) + const bodyTree = this.bodyParser.parse(snippet) snippet = new Snippet({ name: '__anonymous', prefix: '', bodyTree, bodyText: snippet }) } return new SnippetExpansion(snippet, editor, cursor, this) diff --git a/spec/body-parser-spec.js b/spec/body-parser-spec.js index 7af6365e..6d3de48b 100644 --- a/spec/body-parser-spec.js +++ b/spec/body-parser-spec.js @@ -1,10 +1,13 @@ /* eslint no-template-curly-in-string: 0 */ +const fs = require('fs-plus') +const peg = require('pegjs') -const BodyParser = require('../lib/snippet-body-parser') +const grammar = fs.readFileSync(require.resolve('../lib/snippet-body-parser.pegjs'), 'utf8') +const bodyParser = peg.generate(grammar) 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 bodyTree = bodyParser.parse(`\ the quick brown $1fox \${2:jumped \${3:over} }the \${4:lazy} dog\ ` @@ -29,7 +32,7 @@ the quick brown $1fox \${2:jumped \${3:over} }) 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 = bodyParser.parse('module ${1:ActiveRecord::${TM_FILENAME/(?:\\A|_)([A-Za-z0-9]+)(?:\\.rb)?/(?2::\\u$1)/g}}') expect(bodyTree).toEqual([ 'module ', { @@ -40,7 +43,7 @@ the quick brown $1fox \${2:jumped \${3:over} }) it('skips escaped tabstops', () => { - const bodyTree = BodyParser.parse('snippet $1 escaped \\$2 \\\\$3') + const bodyTree = bodyParser.parse('snippet $1 escaped \\$2 \\\\$3') expect(bodyTree).toEqual([ 'snippet ', { @@ -56,7 +59,7 @@ the quick brown $1fox \${2:jumped \${3:over} }) it('includes escaped right-braces', () => { - const bodyTree = BodyParser.parse('snippet ${1:{\\}}') + const bodyTree = bodyParser.parse('snippet ${1:{\\}}') expect(bodyTree).toEqual([ 'snippet ', { @@ -67,7 +70,7 @@ the quick brown $1fox \${2:jumped \${3:over} }) it('parses a snippet with transformations', () => { - const bodyTree = BodyParser.parse('<${1:p}>$0') + const bodyTree = bodyParser.parse('<${1:p}>$0') expect(bodyTree).toEqual([ '<', { index: 1, content: ['p'] }, @@ -80,7 +83,7 @@ the quick brown $1fox \${2:jumped \${3:over} }) 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') + const bodyTree = bodyParser.parse('${1:placeholder} ${1/(.)/\\u$1/} $1 ${2:ANOTHER} ${2/^(.*)$/\\L$1/} $2') expect(bodyTree).toEqual([ { index: 1, content: ['placeholder'] }, ' ', @@ -117,7 +120,7 @@ the quick brown $1fox \${2:jumped \${3:over} }) it('parses a snippet with transformations and mirrors', () => { - const bodyTree = BodyParser.parse('${1:placeholder}\n${1/(.)/\\u$1/}\n$1') + const bodyTree = bodyParser.parse('${1:placeholder}\n${1/(.)/\\u$1/}\n$1') expect(bodyTree).toEqual([ { index: 1, content: ['placeholder'] }, '\n', @@ -138,7 +141,7 @@ the quick brown $1fox \${2:jumped \${3:over} }) it('parses a snippet with a format string and case-control flags', () => { - const bodyTree = BodyParser.parse('<${1:p}>$0') + const bodyTree = bodyParser.parse('<${1:p}>$0') expect(bodyTree).toEqual([ '<', { index: 1, content: ['p'] }, @@ -164,7 +167,7 @@ the quick brown $1fox \${2:jumped \${3:over} it('parses a snippet with an escaped forward slash in a transform', () => { // Annoyingly, a forward slash needs to be double-backslashed just like the // other escapes. - const bodyTree = BodyParser.parse('<${1:p}>$0') + const bodyTree = bodyParser.parse('<${1:p}>$0') expect(bodyTree).toEqual([ '<', { index: 1, content: ['p'] }, @@ -188,7 +191,7 @@ the quick brown $1fox \${2:jumped \${3:over} }) it("parses a snippet with a placeholder that mirrors another tab stop's content", () => { - const bodyTree = BodyParser.parse("$4console.${3:log}('${2:$1}', $1);$0") + const bodyTree = bodyParser.parse("$4console.${3:log}('${2:$1}', $1);$0") expect(bodyTree).toEqual([ { index: 4, content: [] }, 'console.', @@ -208,7 +211,7 @@ the quick brown $1fox \${2:jumped \${3:over} }) 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") + const bodyTree = bodyParser.parse("$4console.${3:log}('${2:uh $1}', $1);$0") expect(bodyTree).toEqual([ { index: 4, content: [] }, 'console.', diff --git a/spec/snippets-spec.js b/spec/snippets-spec.js index 0207cebf..9c1fae84 100644 --- a/spec/snippets-spec.js +++ b/spec/snippets-spec.js @@ -292,27 +292,20 @@ third tabstop $3\ }) it('parses snippets once, reusing cached ones on subsequent queries', () => { - spyOn(Snippets, 'getBodyParser').andCallThrough() - + // TODO: Once Jasmin >= 2.6.1, ensure the property is invoked when appropriate editor.insertText('t1') simulateTabKeyEvent() - expect(Snippets.getBodyParser).toHaveBeenCalled() expect(editor.lineTextForBufferRow(0)).toBe('this is a testvar quicksort = function () {') expect(editor.getCursorScreenPosition()).toEqual([0, 14]) - Snippets.getBodyParser.reset() - editor.setText('') editor.insertText('t1') simulateTabKeyEvent() - expect(Snippets.getBodyParser).not.toHaveBeenCalled() expect(editor.lineTextForBufferRow(0)).toBe('this is a test') expect(editor.getCursorScreenPosition()).toEqual([0, 14]) - Snippets.getBodyParser.reset() - Snippets.add(__filename, { '.source.js': { 'invalidate previous snippet': { @@ -326,7 +319,6 @@ third tabstop $3\ editor.insertText('t1') simulateTabKeyEvent() - expect(Snippets.getBodyParser).toHaveBeenCalled() expect(editor.lineTextForBufferRow(0)).toBe('new snippet') expect(editor.getCursorScreenPosition()).toEqual([0, 11]) }) From 6c5227a37935465f055045d9688a2ac1b436a98f Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Sun, 19 Jul 2020 02:00:11 +0300 Subject: [PATCH 08/17] yes --- lib/snippet-body-parser.pegjs | 191 ++++++++++++++-------------- lib/snippet-expansion.js | 4 + lib/snippet.js | 52 +------- lib/snippets.js | 111 ++++++++--------- lib/variable-registery.js | 8 ++ lib/variable.js | 8 ++ spec/body-parser-spec.js | 226 ++++++++++++++++++---------------- 7 files changed, 284 insertions(+), 316 deletions(-) create mode 100644 lib/variable-registery.js create mode 100644 lib/variable.js diff --git a/lib/snippet-body-parser.pegjs b/lib/snippet-body-parser.pegjs index d0129025..a6a6a8b9 100644 --- a/lib/snippet-body-parser.pegjs +++ b/lib/snippet-body-parser.pegjs @@ -1,123 +1,112 @@ { - // 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); - }, []); - } + const upperFirst = (string, locale = navigator.language) => + string.replace(/^\p{CWU}/u, (character) => character.toLocaleUpperCase(locale)); + const lowerFirst = (string, locale = navigator.language) => + string.replace(/^\p{CWU}/u, (character) => character.toLocaleLowerCase(locale)); + const toUpper = (string, locale = navigator.language) => + string.toLocaleUpperCase(locale); + const toLower = (string, locale = navigator.language) => + string.toLocaleLowerCase(locale); } -bodyContent = content:(tabStop / bodyContentText)* { return content; } +Snippet + = snippet:(PlainText / Variable)* { + return new Snippet({ value: snippet, range: range(), registery }); + } -bodyContentText = text:bodyContentChar+ { return text.join(''); } +PlainText + = plaintext:(@EscapeSequence / !Variable @.)+ { return plaintext.join(''); } -bodyContentChar - = escaped - / !tabStop char:. { return char; } +Variable + = "$" variable:(Identifier / Expression) { + return registery.add(new Variable({ ...variable, range: range() })); + } -escaped = "\\" char:. { return char; } +Identifier + = identifier:$( + Integer + / (character:. & { return /[\p{L}\d_]/u.test(character); })+ + ) { return { identifier }; } -tabStop - = tabStopWithTransformation - / tabStopWithPlaceholder - / tabStopWithoutPlaceholder - / simpleTabStop +Expression + = "{" + identifier:Identifier + expression:(Placeholder / Choice / Transformation)? + "}" { return { ...identifier, ...expression }; } -simpleTabStop - = "$" index:[0-9]+ { - return { index: parseInt(index.join('')), content: [] }; - } +Placeholder = ":" value:(Value / Variable)+ { return { value }; } -tabStopWithoutPlaceholder - = "${" index:[0-9]+ "}" { - return { index: parseInt(index.join('')), content: [] }; - } +Value + = characters:(@EscapeSequence / !Variable @[^}])+ { return characters.join(''); } -tabStopWithPlaceholder - = "${" index:[0-9]+ ":" content:placeholderContent "}" { - return { index: parseInt(index.join('')), content: content }; - } +Choice = "|" choices:(@Selection ","?)+ "|" { return { value:[choices] }; } -tabStopWithTransformation - = "${" index:[0-9]+ substitution:transformationSubstitution "}" { +Selection + = characters:(@EscapeSequence / !Variable @[^,|}])+ { return characters.join(''); } + +Transformation + = "/" regexp:$[^/}]+ "/" format:Format "/" flags:$[gimsuy]* { return { - index: parseInt(index.join(''), 10), - content: [], - substitution: substitution, + format, + transformation: (value) => { + let fold = (sequence) => sequence; + regexp = new RegExp(regexp, flags); + return format.reduce((result, sequence) => { + if (sequence instanceof Function) { + fold = sequence; + } else if (Array.isArray(sequence)) { + const [group, insertion, replacement = ''] = sequence; + result += fold(value.replace(regexp, group) ? insertion : replacement); + } else { + result += fold(value.replace(regexp, sequence), value, regexp); + } + return result; + }, ''); + }, }; } -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: [] }; +Format + = ( + CaseFold + / ConditionalInsert + / Replacement + )+ + / "" { return ['']; } + +Replacement = characters:(@EscapeSequence / !CaseFold !ConditionalInsert @[^/}])+ { return characters.join(''); } + +CaseFold + = "\\E" { return (sequence) => sequence; } + / "\\u" { return upperFirst; } + / "\\l" { return lowerFirst; } + / "\\U" { return toUpper; } + / "\\L" { return toLower; } + +ConditionalInsert + = "(?" group:$Integer insertion:Insertion replacement:Insertion? ")" { + return [`$${group}`, insertion, replacement != null ? replacement : undefined]; } -variable - = "${" variableContent "}" { - return ''; // we eat variables and do nothing with them for now +Insertion + = ":" characters:(@EscapeSequence / @[^:)])+ { return characters.join(''); } + +EscapeSequence + = "\\n" { return '\n'; } + / "\\r" { return '\r'; } + / "\\v" { return '\v'; } + / "\\t" { return '\t'; } + / "\\b" { return '\b'; } + / "\\f" { return '\f'; } + / "\\u" codepoint:(UTF16 / UTF32) { + return String.fromCodePoint(Number.parseInt(codepoint, 16)); } + / "\\" @. -variableContent = content:(variable / variableContentText)* { return content; } - -variableContentText = text:variableContentChar+ { return text.join(''); } +UTF16 = $(HexDigit HexDigit HexDigit HexDigit) -variableContentChar = !variable char:("\\}" / [^}]) { return char; } +UTF32 = "{" @$(HexDigit HexDigit? HexDigit? HexDigit? HexDigit? HexDigit?) "}" -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) }; - } +HexDigit = [0-9a-f]i -// One of the special control flags in a format string for case folding and -// other tasks. -formatStringEscape = "\\" flag:[ULulErn$] { return { escape: flag }; } +Integer = [0-9]+ diff --git a/lib/snippet-expansion.js b/lib/snippet-expansion.js index 00cfbc56..37c74a92 100644 --- a/lib/snippet-expansion.js +++ b/lib/snippet-expansion.js @@ -41,6 +41,10 @@ module.exports = class SnippetExpansion { }) } + addExpansion (editor, snippetExpansion) { + this.getStore(editor).addExpansion(snippetExpansion) + } + // 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) { diff --git a/lib/snippet.js b/lib/snippet.js index 8ff6ce7e..a239e72e 100644 --- a/lib/snippet.js +++ b/lib/snippet.js @@ -1,56 +1,14 @@ -const { Range } = require('atom') -const TabStopList = require('./tab-stop-list') +const Variable = require('./variable.js') -module.exports = class Snippet { - constructor ({ name, prefix, bodyText, description, descriptionMoreURL, rightLabelHTML, leftLabel, leftLabelHTML, bodyTree }) { - this.name = name +module.exports = class Snippet extends Variable { + constructor ({ identifier = '__anonymous', prefix = '', registery, description, descriptionMoreURL, rightLabelHTML, leftLabel, leftLabelHTML, value, range }) { + super({ identifier, value, range }) this.prefix = prefix - this.bodyText = bodyText + this.registery = registery 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 - const 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.js b/lib/snippets.js index 404fceaa..bacf2192 100644 --- a/lib/snippets.js +++ b/lib/snippets.js @@ -1,5 +1,5 @@ const path = require('path') -const { Emitter, Disposable, CompositeDisposable, File } = require('atom') +const {Emitter, Disposable, CompositeDisposable, File} = require('atom') const _ = require('underscore-plus') const async = require('async') const CSON = require('season') @@ -10,27 +10,27 @@ const ScopedPropertyStore = require('scoped-property-store') const Snippet = require('./snippet') const SnippetExpansion = require('./snippet-expansion') const EditorStore = require('./editor-store') -const { getPackageRoot } = require('./helpers') +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.snippetsByPackage = new Map + this.parsedSnippetsById = new Map + this.editorMarkerLayers = new WeakMap - this.scopedPropertyStore = new ScopedPropertyStore() + 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.disabledSnippetsScopedPropertyStore = new ScopedPropertyStore - this.subscriptions = new CompositeDisposable() + this.subscriptions = new CompositeDisposable this.subscriptions.add(atom.workspace.addOpener(uri => { if (uri === 'atom://.atom/snippets') { return atom.workspace.openTextFile(this.getUserSnippetsPath()) @@ -42,14 +42,14 @@ module.exports = { this.subscriptions.add(watchDisposable) }) - this.subscriptions.add(atom.config.onDidChange('core.packagesWithSnippetsDisabled', ({ newValue, oldValue }) => { - this.handleDisabledPackagesDidChange(newValue, oldValue) + 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) { + 'snippets:expand'(event) { const editor = this.getModel() if (snippets.snippetToExpandUnderCursor(editor)) { snippets.clearExpansions(editor) @@ -59,17 +59,17 @@ module.exports = { } }, - 'snippets:next-tab-stop' (event) { + 'snippets:next-tab-stop'(event) { const editor = this.getModel() if (!snippets.goToNextTabStop(editor)) { event.abortKeyBinding() } }, - 'snippets:previous-tab-stop' (event) { + 'snippets:previous-tab-stop'(event) { const editor = this.getModel() if (!snippets.goToPreviousTabStop(editor)) { event.abortKeyBinding() } }, - 'snippets:available' (event) { + 'snippets:available'(event) { const editor = this.getModel() const SnippetsAvailable = require('./snippets-available') if (snippets.availableSnippetsView == null) { snippets.availableSnippetsView = new SnippetsAvailable(snippets) } @@ -88,7 +88,7 @@ module.exports = { }, get bodyParser () { - delete this.bodyParser + delete this.bodyParser; const grammar = fs.readFileSync(require.resolve('./snippet-body-parser.pegjs'), 'utf8') return (this.bodyParser = peg.generate(grammar)) }, @@ -103,8 +103,8 @@ module.exports = { loadAll () { this.loadBundledSnippets(bundledSnippets => { - this.loadPackageSnippets((packageSnippets = {}) => { - this.loadUserSnippets((userSnippets = {}) => { + this.loadPackageSnippets(packageSnippets => { + this.loadUserSnippets(userSnippets => { atom.config.transact(() => { for (const snippetSet of [bundledSnippets, packageSnippets, userSnippets]) { for (const filepath in snippetSet) { @@ -121,7 +121,7 @@ module.exports = { loadBundledSnippets (callback) { const bundledSnippetsPath = CSON.resolve(path.join(getPackageRoot(), 'lib', 'snippets')) - this.loadSnippetsFile(bundledSnippetsPath, (snippets = {}) => { + this.loadSnippetsFile(bundledSnippetsPath, snippets => { const snippetsByPath = {} snippetsByPath[bundledSnippetsPath] = snippets callback(snippetsByPath) @@ -131,14 +131,14 @@ module.exports = { loadUserSnippets (callback) { const userSnippetsPath = this.getUserSnippetsPath() fs.stat(userSnippetsPath, (error, stat) => { - if (!error && stat.isFile()) { - this.loadSnippetsFile(userSnippetsPath, (snippets = {}) => { + if (stat != null && stat.isFile()) { + this.loadSnippetsFile(userSnippetsPath, snippets => { const result = {} result[userSnippetsPath] = snippets callback(result) }) } else { - callback() + callback({}) } }) }, @@ -146,7 +146,7 @@ module.exports = { watchUserSnippets (callback) { const userSnippetsPath = this.getUserSnippetsPath() fs.stat(userSnippetsPath, (error, stat) => { - if (!error && stat.isFile()) { + if (stat != null && stat.isFile()) { const userSnippetsFileDisposable = new CompositeDisposable() const userSnippetsFile = new File(userSnippetsPath) try { @@ -162,7 +162,7 @@ module.exports = { [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 }) + atom.notifications.addError(message, {dismissable: true}) } callback(userSnippetsFileDisposable) @@ -178,7 +178,7 @@ module.exports = { const userSnippetsPath = this.getUserSnippetsPath() atom.config.transact(() => { this.clearSnippetsForPath(userSnippetsPath) - this.loadSnippetsFile(userSnippetsPath, (result = {}) => { + this.loadSnippetsFile(userSnippetsPath, result => { this.add(userSnippetsPath, result) }) }) @@ -234,12 +234,9 @@ module.exports = { } async.map(snippetsDirPaths, this.loadSnippetsDirectory.bind(this), (error, results) => { - if (error) { - callback() - } const zipped = [] for (const key in results) { - zipped.push({ result: results[key], pack: packages[key] }) + zipped.push({result: results[key], pack: packages[key]}) } const enabledPackages = [] @@ -277,7 +274,7 @@ module.exports = { getEmitter () { if (this.emitter == null) { - this.emitter = new Emitter() + this.emitter = new Emitter } return this.emitter }, @@ -296,25 +293,25 @@ module.exports = { entries, (entry, done) => { const filePath = path.join(snippetsDirPath, entry) - this.loadSnippetsFile(filePath, (snippets = {}) => done(null, { filePath, snippets })) + this.loadSnippetsFile(filePath, snippets => done(null, {filePath, snippets})) }, (error, results) => { const snippetsByPath = {} - for (const { filePath, snippets } of results) { + for (const {filePath, snippets} of results) { snippetsByPath[filePath] = snippets } - callback(error, snippetsByPath) - }) + callback(null, snippetsByPath) + }) }) }) }, loadSnippetsFile (filePath, callback) { - if (!CSON.isObjectPath(filePath)) { return callback() } - CSON.readFile(filePath, { allowDuplicateKeys: false }, (error, object = {}) => { + 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 }) + atom.notifications.addError(`Failed to load snippets from '${filePath}'`, {detail: error.message, dismissable: true}) } callback(object) }) @@ -324,10 +321,10 @@ module.exports = { for (const selector in snippetsBySelector) { const snippetsByName = snippetsBySelector[selector] const unparsedSnippetsByPrefix = {} - for (const name in snippetsByName) { - const attributes = snippetsByName[name] + for (const identifier in snippetsByName) { + const attributes = snippetsByName[identifier] const { prefix, body } = attributes - attributes.name = name + attributes.identifier = identifier attributes.id = this.snippetIdCounter++ if (typeof body === 'string') { unparsedSnippetsByPrefix[prefix] = attributes @@ -365,9 +362,9 @@ module.exports = { // 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 } + unparsedSnippets[selector] = {"snippets": value} const store = isDisabled ? this.disabledSnippetsScopedPropertyStore : this.scopedPropertyStore - store.addProperties(path, unparsedSnippets, { priority: this.priorityForSource(path) }) + store.addProperties(path, unparsedSnippets, {priority: this.priorityForSource(path)}) }, clearSnippetsForPath (path) { @@ -387,7 +384,7 @@ module.exports = { const unparsedSnippetsByPrefix = this.scopedPropertyStore.getPropertyValue( this.getScopeChain(scopeDescriptor), - 'snippets' + "snippets" ) const legacyScopeDescriptor = atom.config.getLegacyScopeDescriptorForNewScopeDescriptor @@ -397,7 +394,7 @@ module.exports = { if (legacyScopeDescriptor) { unparsedLegacySnippetsByPrefix = this.scopedPropertyStore.getPropertyValue( this.getScopeChain(legacyScopeDescriptor), - 'snippets' + "snippets" ) } @@ -423,15 +420,14 @@ module.exports = { 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.bodyParser.parse(body) } - snippet = new Snippet({ id, name, prefix, bodyTree, description, descriptionMoreURL, rightLabelHTML, leftLabel, leftLabelHTML, bodyText: body }) - this.parsedSnippetsById.set(attributes.id, snippet) + getParsedSnippet ({ id, body, bodyTree, ...attributes }) { + if (!this.parsedSnippetsById.has(id)) { + this.parsedSnippetsById.set(id, new Snippet({ + ...(bodyTree || this.bodyParser.parse(body)), + ...attributes + })) } - return snippet + return this.parsedSnippetsById.get(id) }, priorityForSource (source) { @@ -456,7 +452,7 @@ module.exports = { for (const cursor of editor.getCursors()) { const position = cursor.getBufferPosition() - const prefixStart = cursor.getBeginningOfCurrentWordBufferPosition({ wordRegex }) + const prefixStart = cursor.getBeginningOfCurrentWordBufferPosition({wordRegex}) const cursorSnippetPrefix = editor.getTextInRange([prefixStart, position]) if ((snippetPrefix != null) && (cursorSnippetPrefix !== snippetPrefix)) { return null } snippetPrefix = cursorSnippetPrefix @@ -467,7 +463,7 @@ module.exports = { wordPrefix = cursorWordPrefix } - return { snippetPrefix, wordPrefix } + return {snippetPrefix, wordPrefix} }, // Get a RegExp of all the characters used in the snippet prefixes @@ -563,7 +559,7 @@ module.exports = { findOrCreateMarkerLayer (editor) { let layer = this.editorMarkerLayers.get(editor) if (layer === undefined) { - layer = editor.addMarkerLayer({ maintainHistory: true }) + layer = editor.addMarkerLayer({maintainHistory: true}) this.editorMarkerLayers.set(editor, layer) } return layer @@ -582,10 +578,6 @@ module.exports = { store.stopObservingHistory() }, - addExpansion (editor, snippetExpansion) { - this.getStore(editor).addExpansion(snippetExpansion) - }, - textChanged (editor, event) { const store = this.getStore(editor) const activeExpansions = store.getExpansions() @@ -626,8 +618,7 @@ module.exports = { if (editor == null) { editor = atom.workspace.getActiveTextEditor() } if (cursor == null) { cursor = editor.getLastCursor() } if (typeof snippet === 'string') { - const bodyTree = this.bodyParser.parse(snippet) - snippet = new Snippet({ name: '__anonymous', prefix: '', bodyTree, bodyText: snippet }) + snippet = new Snippet({ ...this.bodyParser.parse(snippet) }) } return new SnippetExpansion(snippet, editor, cursor, this) }, diff --git a/lib/variable-registery.js b/lib/variable-registery.js new file mode 100644 index 00000000..5b7b0b41 --- /dev/null +++ b/lib/variable-registery.js @@ -0,0 +1,8 @@ +module.exports = class VariableRegistery extends Map { + add (variable) { + this.has(variable.identifier) + ? this.get(variable.identifier).unshift(variable) + : this.set(variable.identifier, [variable]) + return variable + } +} diff --git a/lib/variable.js b/lib/variable.js new file mode 100644 index 00000000..932a5c3a --- /dev/null +++ b/lib/variable.js @@ -0,0 +1,8 @@ +module.exports = class Variable { + constructor ({ identifier, value = [], range, transformation }) { + this.identifier = identifier + this.value = value + this.range = range + this.transformation = transformation + } +} diff --git a/spec/body-parser-spec.js b/spec/body-parser-spec.js index 6d3de48b..a06d0a27 100644 --- a/spec/body-parser-spec.js +++ b/spec/body-parser-spec.js @@ -1,91 +1,102 @@ -/* eslint no-template-curly-in-string: 0 */ 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') -const bodyParser = peg.generate(grammar) -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(`\ + +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 = 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 = 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 = 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') + it("parses a snippet with transformations", () => { + const bodyTree = bodyParser.parse("<${1:p}>$0"); expect(bodyTree).toEqual([ '<', - { index: 1, content: ['p'] }, + {index: 1, content: ['p']}, '>', - { index: 0, content: [] }, + {index: 0, content: []}, '' - ]) - }) + ]); + }); - 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 = 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, @@ -93,15 +104,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, @@ -109,20 +120,21 @@ 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 = bodyParser.parse("${1:placeholder}\n${1/(.)/\\u$1/}\n$1"); expect(bodyTree).toEqual([ - { index: 1, content: ['placeholder'] }, + {index: 1, content: ['placeholder']}, '\n', { index: 1, @@ -130,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') + it("parses a snippet with a format string and case-control flags", () => { + const bodyTree = bodyParser.parse("<${1:p}>$0"); expect(bodyTree).toEqual([ '<', - { index: 1, content: ['p'] }, + {index: 1, content: ['p']}, '>', - { index: 0, content: [] }, + {index: 0, content: []}, '' - ]) - }) + ]); + }); - 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') + const bodyTree = bodyParser.parse("<${1:p}>$0"); expect(bodyTree).toEqual([ '<', - { index: 1, content: ['p'] }, + {index: 1, content: ['p']}, '>', - { index: 0, content: [] }, + {index: 0, content: []}, '' - ]) - }) + ]); + }); it("parses a snippet with a placeholder that mirrors another tab stop's content", () => { - const bodyTree = bodyParser.parse("$4console.${3:log}('${2:$1}', $1);$0") + const bodyTree = bodyParser.parse("$4console.${3:log}('${2:$1}', $1);$0"); expect(bodyTree).toEqual([ - { index: 4, content: [] }, + {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 = 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: []} + ]); + }); +}); From 98c328afef08df188ce49fab0d965a8a6414acd7 Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Sun, 19 Jul 2020 02:23:16 +0300 Subject: [PATCH 09/17] fix lint errors from rebase --- lib/snippets.js | 81 +++++++++--------- spec/body-parser-spec.js | 178 ++++++++++++++++++++------------------- 2 files changed, 131 insertions(+), 128 deletions(-) diff --git a/lib/snippets.js b/lib/snippets.js index bacf2192..13d5ccbf 100644 --- a/lib/snippets.js +++ b/lib/snippets.js @@ -1,5 +1,5 @@ const path = require('path') -const {Emitter, Disposable, CompositeDisposable, File} = require('atom') +const { Emitter, Disposable, CompositeDisposable, File } = require('atom') const _ = require('underscore-plus') const async = require('async') const CSON = require('season') @@ -10,27 +10,27 @@ const ScopedPropertyStore = require('scoped-property-store') const Snippet = require('./snippet') const SnippetExpansion = require('./snippet-expansion') const EditorStore = require('./editor-store') -const {getPackageRoot} = require('./helpers') +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.snippetsByPackage = new Map() + this.parsedSnippetsById = new Map() + this.editorMarkerLayers = new WeakMap() - this.scopedPropertyStore = new ScopedPropertyStore + 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.disabledSnippetsScopedPropertyStore = new ScopedPropertyStore() - this.subscriptions = new CompositeDisposable + this.subscriptions = new CompositeDisposable() this.subscriptions.add(atom.workspace.addOpener(uri => { if (uri === 'atom://.atom/snippets') { return atom.workspace.openTextFile(this.getUserSnippetsPath()) @@ -42,14 +42,14 @@ module.exports = { this.subscriptions.add(watchDisposable) }) - this.subscriptions.add(atom.config.onDidChange('core.packagesWithSnippetsDisabled', ({newValue, oldValue}) => { - this.handleDisabledPackagesDidChange(newValue, oldValue) + 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) { + 'snippets:expand' (event) { const editor = this.getModel() if (snippets.snippetToExpandUnderCursor(editor)) { snippets.clearExpansions(editor) @@ -59,17 +59,17 @@ module.exports = { } }, - 'snippets:next-tab-stop'(event) { + 'snippets:next-tab-stop' (event) { const editor = this.getModel() if (!snippets.goToNextTabStop(editor)) { event.abortKeyBinding() } }, - 'snippets:previous-tab-stop'(event) { + 'snippets:previous-tab-stop' (event) { const editor = this.getModel() if (!snippets.goToPreviousTabStop(editor)) { event.abortKeyBinding() } }, - 'snippets:available'(event) { + 'snippets:available' (event) { const editor = this.getModel() const SnippetsAvailable = require('./snippets-available') if (snippets.availableSnippetsView == null) { snippets.availableSnippetsView = new SnippetsAvailable(snippets) } @@ -88,7 +88,7 @@ module.exports = { }, get bodyParser () { - delete this.bodyParser; + delete this.bodyParser const grammar = fs.readFileSync(require.resolve('./snippet-body-parser.pegjs'), 'utf8') return (this.bodyParser = peg.generate(grammar)) }, @@ -103,8 +103,8 @@ module.exports = { loadAll () { this.loadBundledSnippets(bundledSnippets => { - this.loadPackageSnippets(packageSnippets => { - this.loadUserSnippets(userSnippets => { + this.loadPackageSnippets((packageSnippets = {}) => { + this.loadUserSnippets((userSnippets = {}) => { atom.config.transact(() => { for (const snippetSet of [bundledSnippets, packageSnippets, userSnippets]) { for (const filepath in snippetSet) { @@ -121,7 +121,7 @@ module.exports = { loadBundledSnippets (callback) { const bundledSnippetsPath = CSON.resolve(path.join(getPackageRoot(), 'lib', 'snippets')) - this.loadSnippetsFile(bundledSnippetsPath, snippets => { + this.loadSnippetsFile(bundledSnippetsPath, (snippets = {}) => { const snippetsByPath = {} snippetsByPath[bundledSnippetsPath] = snippets callback(snippetsByPath) @@ -131,14 +131,14 @@ module.exports = { loadUserSnippets (callback) { const userSnippetsPath = this.getUserSnippetsPath() fs.stat(userSnippetsPath, (error, stat) => { - if (stat != null && stat.isFile()) { - this.loadSnippetsFile(userSnippetsPath, snippets => { + if (!error && stat.isFile()) { + this.loadSnippetsFile(userSnippetsPath, (snippets = {}) => { const result = {} result[userSnippetsPath] = snippets callback(result) }) } else { - callback({}) + callback() } }) }, @@ -146,7 +146,7 @@ module.exports = { watchUserSnippets (callback) { const userSnippetsPath = this.getUserSnippetsPath() fs.stat(userSnippetsPath, (error, stat) => { - if (stat != null && stat.isFile()) { + if (!error && stat.isFile()) { const userSnippetsFileDisposable = new CompositeDisposable() const userSnippetsFile = new File(userSnippetsPath) try { @@ -162,7 +162,7 @@ module.exports = { [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}) + atom.notifications.addError(message, { dismissable: true }) } callback(userSnippetsFileDisposable) @@ -178,7 +178,7 @@ module.exports = { const userSnippetsPath = this.getUserSnippetsPath() atom.config.transact(() => { this.clearSnippetsForPath(userSnippetsPath) - this.loadSnippetsFile(userSnippetsPath, result => { + this.loadSnippetsFile(userSnippetsPath, (result = {}) => { this.add(userSnippetsPath, result) }) }) @@ -234,9 +234,10 @@ module.exports = { } async.map(snippetsDirPaths, this.loadSnippetsDirectory.bind(this), (error, results) => { + if (error) callback() const zipped = [] for (const key in results) { - zipped.push({result: results[key], pack: packages[key]}) + zipped.push({ result: results[key], pack: packages[key] }) } const enabledPackages = [] @@ -274,7 +275,7 @@ module.exports = { getEmitter () { if (this.emitter == null) { - this.emitter = new Emitter + this.emitter = new Emitter() } return this.emitter }, @@ -293,25 +294,25 @@ module.exports = { entries, (entry, done) => { const filePath = path.join(snippetsDirPath, entry) - this.loadSnippetsFile(filePath, snippets => done(null, {filePath, snippets})) + this.loadSnippetsFile(filePath, (snippets = {}) => done(null, { filePath, snippets })) }, (error, results) => { const snippetsByPath = {} - for (const {filePath, snippets} of results) { + for (const { filePath, snippets } of results) { snippetsByPath[filePath] = snippets } - callback(null, snippetsByPath) - }) + callback(error, snippetsByPath) + }) }) }) }, loadSnippetsFile (filePath, callback) { - if (!CSON.isObjectPath(filePath)) { return callback({}) } - CSON.readFile(filePath, {allowDuplicateKeys: false}, (error, object = {}) => { + 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}) + atom.notifications.addError(`Failed to load snippets from '${filePath}'`, { detail: error.message, dismissable: true }) } callback(object) }) @@ -362,9 +363,9 @@ module.exports = { // 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} + unparsedSnippets[selector] = { snippets: value } const store = isDisabled ? this.disabledSnippetsScopedPropertyStore : this.scopedPropertyStore - store.addProperties(path, unparsedSnippets, {priority: this.priorityForSource(path)}) + store.addProperties(path, unparsedSnippets, { priority: this.priorityForSource(path) }) }, clearSnippetsForPath (path) { @@ -384,7 +385,7 @@ module.exports = { const unparsedSnippetsByPrefix = this.scopedPropertyStore.getPropertyValue( this.getScopeChain(scopeDescriptor), - "snippets" + 'snippets' ) const legacyScopeDescriptor = atom.config.getLegacyScopeDescriptorForNewScopeDescriptor @@ -394,7 +395,7 @@ module.exports = { if (legacyScopeDescriptor) { unparsedLegacySnippetsByPrefix = this.scopedPropertyStore.getPropertyValue( this.getScopeChain(legacyScopeDescriptor), - "snippets" + 'snippets' ) } @@ -452,7 +453,7 @@ module.exports = { for (const cursor of editor.getCursors()) { const position = cursor.getBufferPosition() - const prefixStart = cursor.getBeginningOfCurrentWordBufferPosition({wordRegex}) + const prefixStart = cursor.getBeginningOfCurrentWordBufferPosition({ wordRegex }) const cursorSnippetPrefix = editor.getTextInRange([prefixStart, position]) if ((snippetPrefix != null) && (cursorSnippetPrefix !== snippetPrefix)) { return null } snippetPrefix = cursorSnippetPrefix @@ -463,7 +464,7 @@ module.exports = { wordPrefix = cursorWordPrefix } - return {snippetPrefix, wordPrefix} + return { snippetPrefix, wordPrefix } }, // Get a RegExp of all the characters used in the snippet prefixes @@ -559,7 +560,7 @@ module.exports = { findOrCreateMarkerLayer (editor) { let layer = this.editorMarkerLayers.get(editor) if (layer === undefined) { - layer = editor.addMarkerLayer({maintainHistory: true}) + layer = editor.addMarkerLayer({ maintainHistory: true }) this.editorMarkerLayers.set(editor, layer) } return layer diff --git a/spec/body-parser-spec.js b/spec/body-parser-spec.js index a06d0a27..0a4953b1 100644 --- a/spec/body-parser-spec.js +++ b/spec/body-parser-spec.js @@ -1,3 +1,5 @@ +/* eslint no-template-curly-in-string: 0 */ + const fs = require('fs-plus') const peg = require('pegjs') @@ -7,20 +9,19 @@ const VariableRegistery = require('../lib/variable-registery') const grammar = fs.readFileSync(require.resolve('../lib/snippet-body-parser.pegjs'), 'utf8') - -describe("Snippet Body Parser", () => { +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", () => { + 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(snippet).toEqual(new Snippet({ - registery : this.registery, + registery: this.registery, range: [0, 61], value: [ 'the quick brown ', @@ -43,60 +44,60 @@ the quick brown $1fox \${2:jumped \${3:over} }) 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"); + it('parses a snippet with transformations', () => { + const bodyTree = this.bodyParser.parse('<${1:p}>$0') expect(bodyTree).toEqual([ '<', - {index: 1, content: ['p']}, + { index: 1, content: ['p'] }, '>', - {index: 0, content: []}, + { index: 0, content: [] }, '' - ]); - }); + ]) + }) - 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, @@ -104,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, @@ -120,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, @@ -142,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"); + it('parses a snippet with a format string and case-control flags', () => { + const bodyTree = this.bodyParser.parse('<${1:p}>$0') expect(bodyTree).toEqual([ '<', - {index: 1, content: ['p']}, + { index: 1, content: ['p'] }, '>', - {index: 0, content: []}, + { index: 0, content: [] }, '' - ]); - }); + ]) + }) - 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"); + const bodyTree = this.bodyParser.parse('<${1:p}>$0') expect(bodyTree).toEqual([ '<', - {index: 1, content: ['p']}, + { index: 1, content: ['p'] }, '>', - {index: 0, content: []}, + { index: 0, content: [] }, '' - ]); - }); + ]) + }) 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: [] } + ]) + }) +}) From 682462519fbc9589368a7032d6d7d6ea40c5c8e9 Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Wed, 22 Jul 2020 14:05:54 +0300 Subject: [PATCH 10/17] this should work we'll see if ive made a huge mistake --- lib/choice.js | 34 +++++ lib/insertion.js | 94 ------------ lib/snippet-body-parser.pegjs | 113 +++++++-------- lib/snippet-expansion.js | 249 -------------------------------- lib/snippet-history-provider.js | 27 ---- lib/snippet.js | 81 +++++++++-- lib/snippets.js | 22 ++- lib/tab-stop-list.js | 48 ------ lib/tab-stop.js | 61 -------- lib/tabstop.js | 33 +++++ lib/variable-registery.js | 8 - lib/variable.js | 38 ++++- package.json | 2 +- 13 files changed, 244 insertions(+), 566 deletions(-) create mode 100644 lib/choice.js delete mode 100644 lib/insertion.js delete mode 100644 lib/snippet-expansion.js delete mode 100644 lib/snippet-history-provider.js delete mode 100644 lib/tab-stop-list.js delete mode 100644 lib/tab-stop.js create mode 100644 lib/tabstop.js delete mode 100644 lib/variable-registery.js diff --git a/lib/choice.js b/lib/choice.js new file mode 100644 index 00000000..13043eb0 --- /dev/null +++ b/lib/choice.js @@ -0,0 +1,34 @@ +const Tabstop = require('./tabstop') + +module.exports = class Choice extends Tabstop { + constructor ({ body, ...variable }) { + super(variable) + // Pluck body out from constructing object, as it's assumed to be an array + this.body = body + this.length = Math.max(...[...this.body.values()].map(choice => choice.length)) + } + + expand (cursor, variables, disposables) { + const index = cursor.editor.buffer.characterIndexForPosition(cursor.getBufferPosition()) + + this.range = this.range.map(offset => + cursor.editor.buffer.positionForCharacterIndex(offset + index).toArray()) + + const marker = cursor.editor.buffer.markRange(this.range) + + disposables.add({ dispose: () => { marker.destroy() } }) + + disposables.add(marker.onDidChange((event, { textChanged, newTailPosition, newHeadPosition } = event) => { + console.log(event) + this.range = [newTailPosition.toArray(), newHeadPosition.toArray()] + if (textChanged) { + console.log(cursor.editor.buffer.getTextInRange(this.range)) + this.mirrors.forEach(mirror => { + console.log(this.identifier, mirror) + }) + } + })) + + return super.expand(cursor, variables, disposables) + } +} diff --git a/lib/insertion.js b/lib/insertion.js deleted file mode 100644 index 874fa0bf..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) { - const flags = { - uppercaseAll: false, - lowercaseAll: false, - uppercaseNext: false, - lowercaseNext: false - } - replace = [...replace] - const 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) { - const transformed = transformText(match[token.backreference], flags) - result.push(transformed) - } - }) - return result.join('') - } - } - - transform (input) { - const { substitution } = this - if (!substitution) { return input } - return input.replace(substitution.find, this.replacer) - } -} - -module.exports = Insertion diff --git a/lib/snippet-body-parser.pegjs b/lib/snippet-body-parser.pegjs index a6a6a8b9..113e88de 100644 --- a/lib/snippet-body-parser.pegjs +++ b/lib/snippet-body-parser.pegjs @@ -1,105 +1,98 @@ { const upperFirst = (string, locale = navigator.language) => - string.replace(/^\p{CWU}/u, (character) => character.toLocaleUpperCase(locale)); + string.replace(/^\p{CWU}/u, character => character.toLocaleUpperCase(locale)) const lowerFirst = (string, locale = navigator.language) => - string.replace(/^\p{CWU}/u, (character) => character.toLocaleLowerCase(locale)); - const toUpper = (string, locale = navigator.language) => - string.toLocaleUpperCase(locale); - const toLower = (string, locale = navigator.language) => - string.toLocaleLowerCase(locale); + string.replace(/^\p{CWU}/u, character => character.toLocaleLowerCase(locale)) + const toUpper = (string, locale = navigator.language) => string.toLocaleUpperCase(locale) + const toLower = (string, locale = navigator.language) => string.toLocaleLowerCase(locale) } -Snippet - = snippet:(PlainText / Variable)* { - return new Snippet({ value: snippet, range: range(), registery }); - } +Snippet = body:(Text / Variable)* { return new Snippet({ body }) } -PlainText - = plaintext:(@EscapeSequence / !Variable @.)+ { return plaintext.join(''); } +Text = text:(@EscapeSequence / !Variable @.)+ { return text.join("") } Variable = "$" variable:(Identifier / Expression) { - return registery.add(new Variable({ ...variable, range: range() })); + switch (true) { + case variable.body instanceof Set: + return new Choice(variable) + case Number.isInteger(variable.identifier): + return new Tabstop(variable) + default: + return new Variable(variable) + } } Identifier - = identifier:$( - Integer - / (character:. & { return /[\p{L}\d_]/u.test(character); })+ - ) { return { identifier }; } + = identifier:(Integer / $(character:. & { return /[\p{L}\d_]/u.test(character) })+) { + return { identifier } + } Expression = "{" identifier:Identifier - expression:(Placeholder / Choice / Transformation)? - "}" { return { ...identifier, ...expression }; } + expression:(& { return !identifier.identifier.length } @Choice / Placeholder / Transformation)? + "}" { return { ...identifier, ...expression } } -Placeholder = ":" value:(Value / Variable)+ { return { value }; } +Placeholder = ":" body:(Value / Variable)+ { return { body } } -Value - = characters:(@EscapeSequence / !Variable @[^}])+ { return characters.join(''); } +Value = text:(@EscapeSequence / !Variable @[^}])+ { return text.join("") } -Choice = "|" choices:(@Selection ","?)+ "|" { return { value:[choices] }; } +Choice = "|" choices:(@Selection ","?)+ "|" { return { body: new Set(choices) } } -Selection - = characters:(@EscapeSequence / !Variable @[^,|}])+ { return characters.join(''); } +Selection = text:(@EscapeSequence / !Variable @[^,|}])+ { return text.join("") } Transformation = "/" regexp:$[^/}]+ "/" format:Format "/" flags:$[gimsuy]* { return { - format, transformation: (value) => { - let fold = (sequence) => sequence; - regexp = new RegExp(regexp, flags); + let fold = (sequence) => sequence + regexp = new RegExp(regexp, flags) return format.reduce((result, sequence) => { if (sequence instanceof Function) { - fold = sequence; + fold = sequence } else if (Array.isArray(sequence)) { - const [group, insertion, replacement = ''] = sequence; - result += fold(value.replace(regexp, group) ? insertion : replacement); + const [group, insertion, replacement = ""] = sequence + result += fold(value.replace(regexp, group) ? insertion : replacement) } else { - result += fold(value.replace(regexp, sequence), value, regexp); + result += fold(value.replace(regexp, sequence), value, regexp) } - return result; - }, ''); - }, - }; + return result + }, "") + } + } } Format - = ( - CaseFold - / ConditionalInsert - / Replacement - )+ - / "" { return ['']; } + = (CaseFold / ConditionalInsert / Replacement)+ + / "" { return [""] } -Replacement = characters:(@EscapeSequence / !CaseFold !ConditionalInsert @[^/}])+ { return characters.join(''); } +Replacement + = text:(@EscapeSequence / !CaseFold !ConditionalInsert @[^/}])+ { return text.join("") } CaseFold - = "\\E" { return (sequence) => sequence; } - / "\\u" { return upperFirst; } - / "\\l" { return lowerFirst; } - / "\\U" { return toUpper; } - / "\\L" { return toLower; } + = "\\E" { return sequence => sequence } + / "\\u" { return upperFirst } + / "\\l" { return lowerFirst } + / "\\U" { return toUpper } + / "\\L" { return toLower } ConditionalInsert - = "(?" group:$Integer insertion:Insertion replacement:Insertion? ")" { - return [`$${group}`, insertion, replacement != null ? replacement : undefined]; + = "(?" group:Integer insertion:Insertion replacement:Insertion? ")" { + return [`$${group}`, insertion, replacement != null ? replacement : undefined] } -Insertion - = ":" characters:(@EscapeSequence / @[^:)])+ { return characters.join(''); } +Insertion = ":" text:(@EscapeSequence / @[^:)])+ { return text.join("") } EscapeSequence - = "\\n" { return '\n'; } - / "\\r" { return '\r'; } - / "\\v" { return '\v'; } - / "\\t" { return '\t'; } - / "\\b" { return '\b'; } - / "\\f" { return '\f'; } + = "\\n" { return "\n" } + / "\\r" { return "\r" } + / "\\v" { return "\v" } + / "\\t" { return "\t" } + / "\\b" { return "\b" } + / "\\f" { return "\f" } / "\\u" codepoint:(UTF16 / UTF32) { - return String.fromCodePoint(Number.parseInt(codepoint, 16)); + return String.fromCodePoint(Number.parseInt(codepoint, 16)) } / "\\" @. @@ -109,4 +102,4 @@ UTF32 = "{" @$(HexDigit HexDigit? HexDigit? HexDigit? HexDigit? HexDigit?) "}" HexDigit = [0-9a-f]i -Integer = [0-9]+ +Integer = integer:$[0-9]+ { return Number.parseInt(integer) } diff --git a/lib/snippet-expansion.js b/lib/snippet-expansion.js deleted file mode 100644 index 37c74a92..00000000 --- a/lib/snippet-expansion.js +++ /dev/null @@ -1,249 +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() - - const 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) - } - }) - }) - }) - } - - addExpansion (editor, snippetExpansion) { - this.getStore(editor).addExpansion(snippetExpansion) - } - - // 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 0c78f3f0..00000000 --- a/lib/snippet-history-provider.js +++ /dev/null @@ -1,27 +0,0 @@ -function wrap (manager, callbacks) { - const 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 index a239e72e..152bb416 100644 --- a/lib/snippet.js +++ b/lib/snippet.js @@ -1,14 +1,71 @@ -const Variable = require('./variable.js') - -module.exports = class Snippet extends Variable { - constructor ({ identifier = '__anonymous', prefix = '', registery, description, descriptionMoreURL, rightLabelHTML, leftLabel, leftLabelHTML, value, range }) { - super({ identifier, value, range }) - this.prefix = prefix - this.registery = registery - this.description = description - this.descriptionMoreURL = descriptionMoreURL - this.rightLabelHTML = rightLabelHTML - this.leftLabel = leftLabel - this.leftLabelHTML = leftLabelHTML +const { CompositeDisposable } = require('atom') + +const Tabstop = require('./tabstop') + +module.exports = class Snippet extends Tabstop { + static getVariables (cursor) { + return {} + } + + constructor (variable) { + super(variable) + + this.disposables = new CompositeDisposable() + } + + get name () { + return this.identifier || '__anonymous' + } + + destroy (cursor = atom.workspace.getActiveTextEditor().getLastCursor()) { + this.disposables.dispose() + // Normalize line endings (why isn't there a method for this?) + cursor.editor.buffer.setTextInRange(this.range, cursor.editor.buffer.getTextInRange(this.range)) + cursor.editor.normalizeTabsInBufferRange(this.range) + } + + expand (cursor, variables = {}) { + // Construct variables from given and global without given variables overriding global ones + variables = { ...variables, ...Snippet.getVariables(cursor) } + + const index = cursor.editor.buffer.characterIndexForPosition(cursor.getBufferPosition()) + + // Insert snippet text without normalizing line endings, so variabe ranges match + cursor.editor.buffer.insert(cursor.getBufferPosition(), this.toString(), { normalizeLineEndings: false }) + + // Generate tabstops and expanded text + this.stops = super.expand(cursor, variables, this.disposables, index) + // shift-tab could be implemented by defining a custom iterator / linked list + // Sorts stops in ascending order, leaving zero last + .sort(({ identifier: a }, { identifier: b }) => !a - !b || a - b) + // Include an ending tabstop if one not present + if (this.stops[this.stops.length - 1].identifier !== 0) { + this.stops.push({ range: [this.range.end, this.range.end] }) + } + + const target = 'atom-text-editor:not([mini])' + const next = `snippets:next-tab-stop-${this.identifier}` + + this.disposables.add( + atom.commands.add(target, next, event => + this.nextStop(cursor) || event.abortKeyBinding()), + atom.keymaps.add(module.filename, { [target]: { tab: next } }), + cursor.onDidChangePosition(({ newBufferPosition }) => { + if (!this.range.containsPoint(newBufferPosition)) { + this.destroy(cursor) + } + })) + + this.nextStop(cursor) + return this + } + + nextStop (cursor) { + const stop = this.stops.shift() + if (stop) { + cursor.selection.setBufferRange(stop.range) + return true + } + this.destroy() } } diff --git a/lib/snippets.js b/lib/snippets.js index 13d5ccbf..d9581e13 100644 --- a/lib/snippets.js +++ b/lib/snippets.js @@ -7,10 +7,14 @@ const fs = require('fs-plus') const peg = require('pegjs') const ScopedPropertyStore = require('scoped-property-store') -const Snippet = require('./snippet') +const { getPackageRoot } = require('./helpers') const SnippetExpansion = require('./snippet-expansion') const EditorStore = require('./editor-store') -const { getPackageRoot } = require('./helpers') + +const Snippet = require('./snippet') +const Variable = require('./variable') +const Tabstop = require('./tabstop') +const Choice = require('./choice') module.exports = { activate () { @@ -90,7 +94,14 @@ module.exports = { get bodyParser () { delete this.bodyParser const grammar = fs.readFileSync(require.resolve('./snippet-body-parser.pegjs'), 'utf8') - return (this.bodyParser = peg.generate(grammar)) + return (this.bodyParser = peg.generate(grammar, { + context: { + Snippet, + Variable, + Tabstop, + Choice + } + })) }, getUserSnippetsPath () { @@ -644,8 +655,13 @@ module.exports = { return results }, + parse (string) { + return this.bodyParser.parse(string) + }, + provideSnippets () { return { + parse: string => this.parse(string), bundledSnippetsLoaded: () => this.loaded, insertSnippet: this.insert.bind(this), snippetsForScopes: this.parsedSnippetsForScopes.bind(this), diff --git a/lib/tab-stop-list.js b/lib/tab-stop-list.js deleted file mode 100644 index 7391b243..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) { - const indices = Object.keys(this.list).sort((a1, a2) => a1 - a2) - indices.forEach(iterator) - } - - getInsertions () { - const results = [] - this.forEachIndex(index => { - results.push(...this.list[index].insertions) - }) - return results - } - - toArray () { - const results = [] - this.forEachIndex(index => { - const 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 e6af3403..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 () { - const any = this.insertions.some(insertion => insertion.isTransformation()) - if (!any) return true - const 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 }) { - const 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) - }) - const initial = insertions.find(insertion => !insertion.isTransformation()) - if (initial) { - insertions.splice(insertions.indexOf(initial), 1) - insertions.unshift(initial) - } - this.insertions = insertions - } - - copyWithIndent (indent) { - const { snippet, index, insertions } = this - const newInsertions = insertions.map(insertion => { - const { range, substitution } = insertion - const 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/lib/tabstop.js b/lib/tabstop.js new file mode 100644 index 00000000..9174752a --- /dev/null +++ b/lib/tabstop.js @@ -0,0 +1,33 @@ +const { Range } = require('atom') + +const Variable = require('./variable') + +module.exports = class Tabstop extends Variable { + constructor (variable) { + super(variable) + + this.mirrors = [] + } + + expand (cursor, variables, disposables, offset) { + this.range = new Range(...[offset, offset + this.length] + .map(index => cursor.editor.buffer.positionForCharacterIndex(index))) + this.marker = cursor.editor.buffer.markRange(this.range) + + disposables.add({ dispose: () => { this.marker.destroy() } }) + + disposables.add(this.marker.onDidChange(({ textChanged, newTailPosition, newHeadPosition }) => { + this.range = new Range(newTailPosition, newHeadPosition) + if (textChanged) { + console.log(this.range) + console.log('"' + cursor.editor.buffer.getTextInRange(this.range) + '"') + console.log(this.identifier) + this.mirrors.forEach(mirror => { + console.log(this.identifier, mirror) + }) + } + })) + + return super.expand(cursor, variables, disposables, offset) + } +} diff --git a/lib/variable-registery.js b/lib/variable-registery.js deleted file mode 100644 index 5b7b0b41..00000000 --- a/lib/variable-registery.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = class VariableRegistery extends Map { - add (variable) { - this.has(variable.identifier) - ? this.get(variable.identifier).unshift(variable) - : this.set(variable.identifier, [variable]) - return variable - } -} diff --git a/lib/variable.js b/lib/variable.js index 932a5c3a..85777d83 100644 --- a/lib/variable.js +++ b/lib/variable.js @@ -1,8 +1,40 @@ module.exports = class Variable { - constructor ({ identifier, value = [], range, transformation }) { + constructor ({ identifier, body = [''], transformation }) { this.identifier = identifier - this.value = value - this.range = range + this.body = body this.transformation = transformation + + this.length = this.body.reduce((length, value) => length + value.length, 0) + } + + transform (value) { + return this.transformation + ? this.transformation(value) + : value + } + + expand (cursor, variables, disposables, offset) { + if (this.identifier in variables) { + this.body = this.transform(variables[this.identifier]) + this.length = this.body.length + offset += this.length + return this.body + } + return Array.isArray(this.body) + ? this.body.flatMap(value => { + if (value instanceof Variable) { + const expansion = value.expand(cursor, variables, disposables, offset) + offset += value.length + return [value, ...expansion] + } + offset += value.length + return [] + }) : '' + } + + toString () { + return Array.isArray(this.body) + ? this.body.reduce((string, value) => string + value, '') + : this.body || '' } } diff --git a/package.json b/package.json index 1f1c7720..b0fec03c 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "atom": "*" }, "providedServices": { - "snippets": { + "snippets-dev": { "description": "Snippets are text shortcuts that can be expanded to their definition.", "versions": { "0.1.0": "provideSnippets" From 273af982da4d0cf35914b4659261e4a68ecd5bc5 Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Sun, 26 Jul 2020 09:47:56 +0300 Subject: [PATCH 11/17] missing mirrors and choices --- keymaps/snippets-1.cson | 2 - keymaps/snippets-2.cson | 6 - lib/editor-store.js | 73 --- lib/helpers.js | 13 - lib/snippet-body-parser.pegjs | 8 +- lib/snippet.js | 15 +- lib/snippets-available.js | 7 +- lib/snippets.js | 755 ++++++-------------------------- package-lock.json | 79 ++-- package.json | 12 +- {lib => snippets}/snippets.cson | 0 spec/snippet-spec.js | 0 12 files changed, 188 insertions(+), 782 deletions(-) delete mode 100644 keymaps/snippets-1.cson delete mode 100644 keymaps/snippets-2.cson delete mode 100644 lib/editor-store.js delete mode 100644 lib/helpers.js rename {lib => snippets}/snippets.cson (100%) create mode 100644 spec/snippet-spec.js 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 461e7747..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/snippet-body-parser.pegjs b/lib/snippet-body-parser.pegjs index 113e88de..e340e1ee 100644 --- a/lib/snippet-body-parser.pegjs +++ b/lib/snippet-body-parser.pegjs @@ -1,10 +1,10 @@ { - const upperFirst = (string, locale = navigator.language) => - string.replace(/^\p{CWU}/u, character => character.toLocaleUpperCase(locale)) - const lowerFirst = (string, locale = navigator.language) => - string.replace(/^\p{CWU}/u, character => character.toLocaleLowerCase(locale)) const toUpper = (string, locale = navigator.language) => string.toLocaleUpperCase(locale) const toLower = (string, locale = navigator.language) => string.toLocaleLowerCase(locale) + const upperFirst = (string, locale = navigator.language) => + string.replace(/^\p{CWU}/u, character => toUpper(character, locale)) + const lowerFirst = (string, locale = navigator.language) => + string.replace(/^\p{CWU}/u, character => toLower(character, locale)) } Snippet = body:(Text / Variable)* { return new Snippet({ body }) } diff --git a/lib/snippet.js b/lib/snippet.js index 152bb416..c44074f9 100644 --- a/lib/snippet.js +++ b/lib/snippet.js @@ -9,12 +9,8 @@ module.exports = class Snippet extends Tabstop { constructor (variable) { super(variable) - this.disposables = new CompositeDisposable() - } - - get name () { - return this.identifier || '__anonymous' + this.stop = 0 } destroy (cursor = atom.workspace.getActiveTextEditor().getLastCursor()) { @@ -48,8 +44,10 @@ module.exports = class Snippet extends Tabstop { this.disposables.add( atom.commands.add(target, next, event => - this.nextStop(cursor) || event.abortKeyBinding()), + this.nextStop(cursor, event.originalEvent.shiftKey) || + event.abortKeyBinding()), atom.keymaps.add(module.filename, { [target]: { tab: next } }), + cursor.onDidDestroy(() => this.destroy()), cursor.onDidChangePosition(({ newBufferPosition }) => { if (!this.range.containsPoint(newBufferPosition)) { this.destroy(cursor) @@ -61,9 +59,8 @@ module.exports = class Snippet extends Tabstop { } nextStop (cursor) { - const stop = this.stops.shift() - if (stop) { - cursor.selection.setBufferRange(stop.range) + if (this.stops[this.stop]) { + cursor.selection.setBufferRange(this.stops[this.stop++].range) return true } this.destroy() diff --git a/lib/snippets-available.js b/lib/snippets-available.js index 7a760a00..3c3e92d7 100644 --- a/lib/snippets-available.js +++ b/lib/snippets-available.js @@ -1,9 +1,8 @@ -/** @babel */ -import _ from 'underscore-plus' -import SelectListView from 'atom-select-list' +const _ = require('underscore-plus') +const SelectListView = require('atom-select-list') -export default class SnippetsAvailable { +module.exports = class SnippetsAvailable { constructor (snippets) { this.panel = null this.snippets = snippets diff --git a/lib/snippets.js b/lib/snippets.js index d9581e13..d38c0533 100644 --- a/lib/snippets.js +++ b/lib/snippets.js @@ -1,99 +1,63 @@ -const path = require('path') -const { Emitter, Disposable, CompositeDisposable, File } = require('atom') -const _ = require('underscore-plus') -const async = require('async') +const { Emitter, CompositeDisposable, File } = require('atom') + const CSON = require('season') -const fs = require('fs-plus') +const path = require('path') const peg = require('pegjs') -const ScopedPropertyStore = require('scoped-property-store') +const fs = require('fs') -const { getPackageRoot } = require('./helpers') -const SnippetExpansion = require('./snippet-expansion') -const EditorStore = require('./editor-store') +const ScopedPropertyStore = require('scoped-property-store') const Snippet = require('./snippet') const Variable = require('./variable') const Tabstop = require('./tabstop') const Choice = require('./choice') -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() - +module.exports = class Snippets { + static async activate () { this.subscriptions = new CompositeDisposable() - this.subscriptions.add(atom.workspace.addOpener(uri => { - if (uri === 'atom://.atom/snippets') { - return atom.workspace.openTextFile(this.getUserSnippetsPath()) - } - })) + this.snippetsByScopes = new ScopedPropertyStore() + this.packageDisposables = new WeakMap() - 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() - } - }, + this.loaded = false + this.emitter = new Emitter() + + this.subscriptions.add( + atom.workspace.addOpener(uri => uri === 'atom://.atom/snippets' + ? atom.workspace.openTextFile(this.userSnippetsPath) + : undefined), + atom.commands.add('atom-text-editor', 'snippets:available', () => { + const editor = atom.workspace.getActiveTextEditor() + this.availableSnippetsView.toggle(editor) + }), + atom.packages.onDidActivatePackage(bundle => this.loadPackage(bundle)), + atom.packages.onDidDeactivatePackage(bundle => this.unloadPackage(bundle))) + + await Promise.all([ + this.loadUserSnippets(), + ...atom.packages.getActivePackages() + .sort(({ path }) => /\/node_modules\//.test(path) ? -1 : 1) + .map(bundle => this.loadPackage(bundle)) + ]) - 'snippets:next-tab-stop' (event) { - const editor = this.getModel() - if (!snippets.goToNextTabStop(editor)) { event.abortKeyBinding() } - }, + this.loaded = true + this.emitter.emit('did-load-snippets') + } - 'snippets:previous-tab-stop' (event) { - const editor = this.getModel() - if (!snippets.goToPreviousTabStop(editor)) { event.abortKeyBinding() } - }, + static get availableSnippetsView () { + delete this.availableSnippetsView - '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) - } - })) - }, + const SnippetsAvailable = require('./snippets-available') + return (this.availableSnippetsView = new SnippetsAvailable(this)) + } - deactivate () { - if (this.emitter != null) { - this.emitter.dispose() - } - this.emitter = null - this.editorSnippetExpansions = null - atom.config.transact(() => this.subscriptions.dispose()) - }, + static deactivate () { + this.emitter.dispose() + this.subscriptions.dispose() + } - get bodyParser () { + static get bodyParser () { delete this.bodyParser - const grammar = fs.readFileSync(require.resolve('./snippet-body-parser.pegjs'), 'utf8') + const grammar = fs.readFileSync(path.join(module.filename, '../snippet-body-parser.pegjs'), 'utf8') return (this.bodyParser = peg.generate(grammar, { context: { Snippet, @@ -102,576 +66,103 @@ module.exports = { Choice } })) - }, - - 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 (!error && 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 (!error && 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')) + static getUserSnippetsPath () { + let userSnippetsPath = path.join(atom.getConfigDirPath(), 'snippets.json') + try { + fs.accessSync(this.userSnippetsPath) + } catch (error) { + userSnippetsPath = path.join(userSnippetsPath, '../snippets.cson') } + return userSnippetsPath + } - async.map(snippetsDirPaths, this.loadSnippetsDirectory.bind(this), (error, results) => { - if (error) callback() - 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)) - }) - }, + static get userSnippetsPath () { + delete this.userSnippetsPath - doneLoading () { - this.loaded = true - this.getEmitter().emit('did-load-snippets') - }, + return (this.userSnippetsPath = this.getUserSnippetsPath()) + } - onDidLoadSnippets (callback) { - this.getEmitter().on('did-load-snippets', callback) - }, + static async loadSnippetsFile (filepath) { + const priority = filepath === this.userSnippetsPath ? 1000 : 0 + return await new Promise((resolve, reject) => + CSON.readFile(filepath, (error, object) => error == null + ? resolve(this.snippetsByScopes.addProperties(filepath, object, { priority })) + : reject(error))) + } - getEmitter () { - if (this.emitter == null) { - this.emitter = new Emitter() + static async loadUserSnippets () { + try { + const userSnippetsFile = new File(this.userSnippetsPath) + if (this.packageDisposables.has(this)) { + this.packageDisposables.get(this).dispose() + } + this.packageDisposables.set(this, new CompositeDisposable( + await this.loadSnippetsFile(this.userSnippetsPath), + userSnippetsFile.onDidChange(() => this.loadUserSnippets()), + userSnippetsFile.onDidDelete(() => { + this.packageDisposables.get(this).dispose() + this.userSnippetsPath = this.getUserSnippetsPath() + }), + userSnippetsFile.onDidRename(() => { + this.packageDisposables.get(this).dispose() + this.userSnippetsPath = this.getUserSnippetsPath() + }))) + } catch (error) { + console.warn(`Error loading snippets from '${this.userSnippetsPath}': ${error.stack != null ? error.stack : error}`) + atom.notifications.addWarning(`\ + Unable to load snippets from \`${this.userSnippetsPath}\`. + Make sure you have permissions to access the directory and file. + `, { detail: error.message, dismissable: true }) } - 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, {}) + static async loadPackage (bundle) { + const snippetsDirectory = path.join(bundle.path, 'snippets') + try { + const files = await fs.promises.readdir(snippetsDirectory) + files.forEach(async file => { + const snippetsFile = path.join(snippetsDirectory, file) + try { + const disposable = await this.loadSnippetsFile(snippetsFile) + this.packageDisposables.has(bundle) + ? this.packageDisposables.get(bundle).add(disposable) + : this.packageDisposables.set(bundle, new CompositeDisposable(disposable)) + } catch (error) { + console.warn(`Error loading snippets from '${snippetsFile}': ${error.stack != null ? error.stack : error}`) + atom.notifications.addWarning(`\ + Unable to load snippets from \`${snippetsFile}\`. + Make sure you have permissions to access the directory and file. + `, { detail: error.message, dismissable: true }) } - - 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(error, 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 identifier in snippetsByName) { - const attributes = snippetsByName[identifier] - const { prefix, body } = attributes - attributes.identifier = identifier - 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 ({ id, body, bodyTree, ...attributes }) { - if (!this.parsedSnippetsById.has(id)) { - this.parsedSnippetsById.set(id, new Snippet({ - ...(bodyTree || this.bodyParser.parse(body)), - ...attributes - })) - } - return this.parsedSnippetsById.get(id) - }, - - priorityForSource (source) { - if (source === this.getUserSnippetsPath()) { - return 1000 - } else { - return 0 - } - }, - - // 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 - } + } catch (error) { + if (error.code === 'ENOTDIR' || error.code === 'ENOENT') { + // Path either doesn't exist, or isn't a directory + return } + console.warn(`Error reading snippets directory ${snippetsDirectory}`, error) } + } - 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() - }, - - 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') { - snippet = new Snippet({ ...this.bodyParser.parse(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) - } + static unloadPackage (bundle) { + if (this.packageDisposables.has(bundle)) { + this.packageDisposables.get(bundle).dispose() + this.packageDisposables.delete(bundle) } + } - iterate(this.scopedPropertyStore.propertySets) - iterate(this.disabledSnippetsScopedPropertyStore.propertySets) - return results - }, - - parse (string) { - return this.bodyParser.parse(string) - }, + static onDidLoadSnippets (callback) { + this.emitter.on('did-load-snippets', callback) + } - provideSnippets () { + static snippets () { return { - parse: string => this.parse(string), - bundledSnippetsLoaded: () => this.loaded, - insertSnippet: this.insert.bind(this), - snippetsForScopes: this.parsedSnippetsForScopes.bind(this), - getUnparsedSnippets: this.getUnparsedSnippets.bind(this), - getUserSnippetsPath: this.getUserSnippetsPath.bind(this) + parse: string => this.bodyParser.parse(string), + userSnippetsPath: () => this.userSnippetsPath, + snippets: () => this.snippetsByScopes, + loaded: () => this.loaded } - }, - - onUndoOrRedo (editor, isUndo) { - const activeExpansions = this.getExpansions(editor) - activeExpansions.forEach(expansion => expansion.onUndoOrRedo(isUndo)) } } diff --git a/package-lock.json b/package-lock.json index 3dfb7dba..248e0a37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "snippets", - "version": "1.6.0", + "name": "snippets-dev", + "version": "2.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -117,11 +117,6 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, "atom-select-list": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/atom-select-list/-/atom-select-list-0.7.2.tgz", @@ -399,39 +394,41 @@ } }, "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" } } } @@ -867,6 +864,21 @@ "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.0.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", + "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==" + } + } + }, "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -1411,11 +1423,6 @@ "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, - "loophole": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loophole/-/loophole-1.1.0.tgz", - "integrity": "sha1-N5Sf6kU7YlasxyXDIM4MWn9wor0=" - }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -1475,6 +1482,11 @@ "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=" + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -2266,6 +2278,11 @@ "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", "dev": true }, + "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.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", diff --git a/package.json b/package.json index b0fec03c..0ed0a5d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "snippets", - "version": "1.6.0", + "name": "snippets-dev", + "version": "2.0.0", "description": "Expand snippets matching the current prefix with `tab`.", "repository": "https://github.com/atom/snippets", "license": "MIT", @@ -11,14 +11,10 @@ "test": "npm run lint:fix && atom --test spec" }, "dependencies": { - "async": "^3.2.0", "atom-select-list": "^0.7.0", - "fs-plus": "^3.1.1", - "loophole": "^1", "pegjs": "^0.11.0-master.b7b87ea", "scoped-property-store": "^0.17.0", - "season": "^6.0.2", - "underscore-plus": "^1.7.0" + "season": "^6.0.2" }, "devDependencies": { "standard": "^14.3.4", @@ -31,7 +27,7 @@ "snippets-dev": { "description": "Snippets are text shortcuts that can be expanded to their definition.", "versions": { - "0.1.0": "provideSnippets" + "0.2.0": "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/snippet-spec.js b/spec/snippet-spec.js new file mode 100644 index 00000000..e69de29b From 53670532af709906b18c6b931512d0be9b2c540e Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Mon, 14 Sep 2020 08:27:49 +0300 Subject: [PATCH 12/17] i think it's done needs: - tests - documentations (will result in some extensive manual testing) - choices (not sure how the dropdownlist will be implemented) --- lib/choice.js | 34 - lib/constructs/construct.js | 29 + lib/constructs/snippet.js | 257 ++++ lib/constructs/tabstop.js | 8 + lib/constructs/variable.js | 10 + lib/modifiers/choice.js | 41 + lib/modifiers/modifier.js | 7 + lib/modifiers/placeholder.js | 35 + lib/modifiers/transformation.js | 51 + lib/parser/config.js | 13 + lib/parser/snippet-body-parser.js | 1723 ++++++++++++++++++++++++++ lib/parser/snippet-body-parser.pegjs | 91 ++ lib/snippet-body-parser.pegjs | 105 -- lib/snippet.js | 68 - lib/snippets.js | 46 +- lib/tabstop-list.js | 23 + lib/tabstop.js | 33 - lib/variable.js | 40 - package-lock.json | 3 +- package.json | 8 +- 20 files changed, 2311 insertions(+), 314 deletions(-) delete mode 100644 lib/choice.js create mode 100644 lib/constructs/construct.js create mode 100644 lib/constructs/snippet.js create mode 100644 lib/constructs/tabstop.js create mode 100644 lib/constructs/variable.js create mode 100644 lib/modifiers/choice.js create mode 100644 lib/modifiers/modifier.js create mode 100644 lib/modifiers/placeholder.js create mode 100644 lib/modifiers/transformation.js create mode 100644 lib/parser/config.js create mode 100644 lib/parser/snippet-body-parser.js create mode 100644 lib/parser/snippet-body-parser.pegjs delete mode 100644 lib/snippet-body-parser.pegjs delete mode 100644 lib/snippet.js create mode 100644 lib/tabstop-list.js delete mode 100644 lib/tabstop.js delete mode 100644 lib/variable.js diff --git a/lib/choice.js b/lib/choice.js deleted file mode 100644 index 13043eb0..00000000 --- a/lib/choice.js +++ /dev/null @@ -1,34 +0,0 @@ -const Tabstop = require('./tabstop') - -module.exports = class Choice extends Tabstop { - constructor ({ body, ...variable }) { - super(variable) - // Pluck body out from constructing object, as it's assumed to be an array - this.body = body - this.length = Math.max(...[...this.body.values()].map(choice => choice.length)) - } - - expand (cursor, variables, disposables) { - const index = cursor.editor.buffer.characterIndexForPosition(cursor.getBufferPosition()) - - this.range = this.range.map(offset => - cursor.editor.buffer.positionForCharacterIndex(offset + index).toArray()) - - const marker = cursor.editor.buffer.markRange(this.range) - - disposables.add({ dispose: () => { marker.destroy() } }) - - disposables.add(marker.onDidChange((event, { textChanged, newTailPosition, newHeadPosition } = event) => { - console.log(event) - this.range = [newTailPosition.toArray(), newHeadPosition.toArray()] - if (textChanged) { - console.log(cursor.editor.buffer.getTextInRange(this.range)) - this.mirrors.forEach(mirror => { - console.log(this.identifier, mirror) - }) - } - })) - - return super.expand(cursor, variables, disposables) - } -} diff --git a/lib/constructs/construct.js b/lib/constructs/construct.js new file mode 100644 index 00000000..3ee69482 --- /dev/null +++ b/lib/constructs/construct.js @@ -0,0 +1,29 @@ +module.exports = class Construct { + constructor (identifier) { + this.identifier = identifier + } + + write (buffer, range, value) { + return buffer.setTextInRange(range, value) + } + + insert (buffer, position, value) { + return buffer.insert(position, value) + } + + deactivate (marker, buffer) { + marker.destroy() + } + + activate (marker, cursor) { + cursor.selection.setBufferRange(marker.getRange()) + } + + mark ({ layer, start, end = start, exclusive = true, construct = this }) { + layer.markRange({ start, end }, { exclusive }).setProperties({ construct }) + } + + toString () { + return '' + } +} diff --git a/lib/constructs/snippet.js b/lib/constructs/snippet.js new file mode 100644 index 00000000..8073e5f7 --- /dev/null +++ b/lib/constructs/snippet.js @@ -0,0 +1,257 @@ +const { CompositeDisposable } = require('atom') +const path = require('path') + +const TabstopList = require('../tabstop-list') +const Construct = require('./construct') + +module.exports = class Snippet extends Construct { + static getVariables (buffer, cursor, variables) { + // Lazy getters for each variable + return { + ...variables, + // The currently selected text or the empty string + get TM_SELECTED_TEXT () { + delete this.TM_SELECTED_TEXT + return (this.TM_SELECTED_TEXT = cursor.selection.getText()) + }, + // The contents of the current line + get TM_CURRENT_LINE () { + delete this.TM_CURRENT_LINE + return (this.TM_CURRENT_LINE = cursor.getCurrentBufferLine()) + }, + // The contents of the word under cursor or the empty string + get TM_CURRENT_WORD () { + delete this.TM_CURRENT_WORD + return (this.TM_CURRENT_WORD = buffer.getTextInRange(cursor.getCurrentWordBufferRange())) + }, + // The zero-index based line number + get TM_LINE_INDEX () { + delete this.TM_LINE_INDEX + return (this.TM_LINE_INDEX = cursor.getBufferRow().toString()) + }, + // The one-index based line number + get TM_LINE_NUMBER () { + delete this.TM_LINE_NUMBER + // Does 'getScreenRow'work as intended? + return (this.TM_LINE_NUMBER = (cursor.getScreenRow() + 1).toString()) + }, + // The filename of the current document + get TM_FILENAME () { + delete this.TM_FILENAME + return (this.TM_FILENAME = path.basename(buffer.getPath())) + }, + // The filename of the current document without its extensions + get TM_FILENAME_BASE () { + delete this.TM_FILENAME_BASE + const filepath = buffer.getPath() + return (this.TM_FILENAME_BASE = path.basename(filepath, path.extname(filepath))) + }, + // The directory of the current document + get TM_DIRECTORY () { + delete this.TM_DIRECTORY + return (this.TM_DIRECTORY = path.dirname(buffer.getPath())) + }, + // The full file path of the current document + get TM_FILEPATH () { + delete this.TM_FILEPATH + return (this.TM_FILEPATH = buffer.getPath()) + }, + // The contents of the clipboard + get CLIPBOARD () { + delete this.CLIPBOARD + return (this.CLIPBOARD = atom.clipboard.read()) + }, + // The name of the opened workspace or folder + get WORKSPACE_NAME () { + delete this.WORKSPACE_NAME + const [projectPath] = atom.project.relativizePath(buffer.getPath()) + return (this.WORKSPACE_NAME = path.basename(projectPath)) + }, + // Insert the current date and time + // The current year + get CURRENT_YEAR () { + delete this.CURRENT_YEAR + return (this.CURRENT_YEAR = new Date().toLocaleString('default', { year: 'numeric' })) + }, + // The current year's last two digits + get CURRENT_YEAR_SHORT () { + delete this.CURRENT_YEAR_SHORT + return (this.CURRENT_YEAR_SHORT = new Date().toLocaleString('default', { year: '2-digit' })) + }, + // The month as two digits + get CURRENT_MONTH () { + delete this.CURRENT_MONTH + return (this.CURRENT_MONTH = new Date().toLocaleString('default', { month: '2-digit' })) + }, + // The full name of the month + get CURRENT_MONTH_NAME () { + delete this.CURRENT_MONTH_NAME + return (this.CURRENT_MONTH_NAME = new Date().toLocaleString('default', { month: 'long' })) + }, + // The short name of the month + get CURRENT_MONTH_NAME_SHORT () { + delete this.CURRENT_MONTH_NAME_SHORT + return (this.CURRENT_MONTH_NAME_SHORT = new Date().toLocaleString('default', { month: 'short' })) + }, + // The day of the month + get CURRENT_DATE () { + delete this.CURRENT_DATE + return (this.CURRENT_DATE = new Date().toLocaleString('default', { day: '2-digit' })) + }, + // The name of day + get CURRENT_DAY_NAME () { + delete this.CURRENT_DAY_NAME + return (this.CURRENT_DAY_NAME = new Date().toLocaleString('default', { weekday: 'long' })) + }, + // The short name of the day + get CURRENT_DAY_NAME_SHORT () { + delete this.CURRENT_DAY_NAME_SHORT + return (this.CURRENT_DAY_NAME_SHORT = new Date().toLocaleString('default', { weekday: 'short' })) + }, + // The current hour in 24-hour clock format + get CURRENT_HOUR () { + delete this.CURRENT_HOUR + return (this.CURRENT_HOUR = new Date().toLocaleString('default', { hour: '2-digit' })) + }, + // The current minute + get CURRENT_MINUTE () { + delete this.CURRENT_MINUTE + return (this.CURRENT_MINUTE = new Date().toLocaleString('default', { minute: '2-digit' })) + }, + // The current second + get CURRENT_SECOND () { + delete this.CURRENT_SECOND + return (this.CURRENT_SECOND = new Date().toLocaleString('default', { second: '2-digit' })) + }, + // The number of seconds since the Unix epoch + get CURRENT_SECONDS_UNIX () { + delete this.CURRENT_SECONDS_UNIX + return (this.CURRENT_SECONDS_UNIX = Math.round(new Date() / 1000).toString()) + } + /* + TODO?: + Insert line or block comments, honoring the current language + BLOCK_COMMENT_START + BLOCK_COMMENT_END + LINE_COMMENT + */ + } + } + + // we use static (for memory reasons and)? to enforce that snippets don't alter + // their own internal state once created (so they can be pre-generated and reused) + static expand (snippet, buffer, cursor, variables, editor) { + const disposables = new CompositeDisposable() + + // Construct variables from given and global + Object.defineProperties(variables, Object.getOwnPropertyDescriptors(Snippet.getVariables(buffer, cursor))) + + // Create a new layer to store created tabstops + const layer = buffer.addMarkerLayer({ role: 'tabstops' }) + + disposables.add({ dispose: () => layer.destroy() }) + + // Define a marker that spans the whole snippet + // This will also be used as the ending tabstop if there isn't an explicit one + // Unlike all other tabstops, this marker isn't exclusive, meaning it expands + // with the inserted snippet bellow + snippet.mark({ layer, start: cursor.getBufferPosition(), exclusive: false }) + const [marker] = layer.getMarkers() + + snippet.body.forEach(value => { + value instanceof Object + ? value.expand(buffer, cursor, layer, variables) + : snippet.insert(buffer, cursor.getBufferPosition(), value) + }) + + // Only create tabstop stuff if we have any + if (layer.getMarkerCount() > 1) { + // The underlying data-structure is an Object, so the markers aren't guaranteed + // to be in insertion order. in reality they will be, as the ids used are integers + const tabstops = new TabstopList(layer.getMarkers()).entries() + + const nextStop = () => { + const next = tabstops.next() + if (next.done) { + return disposables.dispose() + } + + const { value: [id, [stopId, ...mirrorIds] = []] } = next + + const previous = tabstops[id - 1] || [] + + previous.forEach(stopId => { + const stop = layer.getMarker(stopId) + stop.getProperties().construct.deactivate(stop, buffer) + }) + + const stop = layer.getMarker(stopId) + const { construct } = stop.getProperties() + + construct.activate(stop, cursor) + mirrorIds.forEach(mirrorId => + editor.decorateMarker(layer.getMarker(mirrorId), { type: 'highlight' })) + + return true + } + + const target = 'atom-text-editor:not([mini])' + const next = `snippets:next-tab-stop-${layer.id}` + + disposables.add( + atom.keymaps.add(module.filename, { [target]: { tab: next } }), + atom.commands.add(target, next, event => nextStop() || + event.abortKeyBinding())) + + // Go to the first tabstop + nextStop() + } + + disposables.add( + cursor.onDidDestroy(() => disposables.dispose()), + cursor.onDidChangePosition(({ newBufferPosition }) => { + // Exclude endpoints, so that end tabstops don't trigger mirror logic + if (!marker.getRange().containsPoint(newBufferPosition, true)) { + disposables.dispose() + } + })) + + return marker.getRange() + } + + constructor (body, legacySyntax) { + // This snippet will work as the default ending tabstop + super(0) + + this.body = body + + this.legacySyntax = legacySyntax + } + + // helper cause Snippet isn't really ever available + expand ({ + buffer = atom.workspace.getActiveTextEditor().getBuffer(), + cursor = atom.workspace.getActiveTextEditor().getLastCursor(), + variables = {}, + // We absolutely need the same editor the cursor belongs to + editor = cursor.editor + } = {}) { + if (this.legacySyntax) { + atom.notifications.addWarning('Snippets: Snippet uses deprecated syntax.', { + description: 'Old syntactic features will be removed in a future release', + dismissable: true + }) + } + + return Snippet.expand(this, buffer, cursor, variables, editor) + } + + // We work as the default ending tabstop, this is a special case + activate (marker, cursor) { + cursor.setBufferPosition(marker.getRange().end) + } + + toString () { + return this.body.reduce((result, value) => result + value) + } +} diff --git a/lib/constructs/tabstop.js b/lib/constructs/tabstop.js new file mode 100644 index 00000000..bbed3c90 --- /dev/null +++ b/lib/constructs/tabstop.js @@ -0,0 +1,8 @@ +const Construct = require('./construct') + +module.exports = class Tabstop extends Construct { + expand (buffer, cursor, layer, variables) { + const start = cursor.getBufferPosition() + this.mark({ layer, start }) + } +} diff --git a/lib/constructs/variable.js b/lib/constructs/variable.js new file mode 100644 index 00000000..a017b7eb --- /dev/null +++ b/lib/constructs/variable.js @@ -0,0 +1,10 @@ +const Construct = require('./construct') + +module.exports = class Variable extends Construct { + expand (buffer, cursor, layer, variables) { + const position = cursor.getBufferPosition() + this.identifier in variables + ? this.insert(buffer, position, variables[this.identifier]) + : this.mark({ layer, ...this.insert(buffer, position, this.identifier) }) + } +} diff --git a/lib/modifiers/choice.js b/lib/modifiers/choice.js new file mode 100644 index 00000000..1cd255e2 --- /dev/null +++ b/lib/modifiers/choice.js @@ -0,0 +1,41 @@ +const Modifier = require('./modifier') + +module.exports = class Choice extends Modifier { + constructor ([first, ...rest]) { + super() + + this.default = first + this.choices = rest + } + + modify ([Construct, identifier]) { + class Choice extends Construct { + constructor (identifier, { choices }) { + super(identifier) + + this.choices = choices + } + + activate (marker, cursor) { + cursor.selection.setBufferRange(marker.getRange()) + // todo dropdownlist + } + + expand (buffer, cursor, layer, variables) { + if (this.identifier in variables && !variables[this.identifier]) { + const position = cursor.getBufferPosition() + layer.markRange(this.insert(buffer, position, this.default)) + .setProperties({ construct: this }) + } else { + super.expand(buffer, cursor, layer, variables) + } + } + + toString () { + return this.default.toString() + } + } + + return new Choice(identifier, this) + } +} diff --git a/lib/modifiers/modifier.js b/lib/modifiers/modifier.js new file mode 100644 index 00000000..f004492a --- /dev/null +++ b/lib/modifiers/modifier.js @@ -0,0 +1,7 @@ +module.exports = class Modifier { + modify ([Construct, identifier]) { + class Modifier extends Construct {} + + return new Modifier(identifier) + } +} diff --git a/lib/modifiers/placeholder.js b/lib/modifiers/placeholder.js new file mode 100644 index 00000000..8943b671 --- /dev/null +++ b/lib/modifiers/placeholder.js @@ -0,0 +1,35 @@ +const Modifier = require('./modifier') + +module.exports = class Placeholder extends Modifier { + constructor (snippet) { + super() + + this.snippet = snippet + // Set to false, so the notification doesn't get created multiple times + this.snippet.legacySyntax = false + } + + modify ([Construct, identifier]) { + class Placeholder extends Construct { + constructor (identifier, { snippet }) { + super(identifier) + + this.snippet = snippet + } + + expand (buffer, cursor, layer, variables) { + if (!(this.identifier in variables)) { + this.mark({ layer, ...this.snippet.expand({ buffer, cursor, variables }) }) + } else { + super.expand(buffer, cursor, layer, variables) + } + } + + toString () { + return this.snippet.toString() + } + } + + return new Placeholder(identifier, this) + } +} diff --git a/lib/modifiers/transformation.js b/lib/modifiers/transformation.js new file mode 100644 index 00000000..7d2e940d --- /dev/null +++ b/lib/modifiers/transformation.js @@ -0,0 +1,51 @@ +const Modifier = require('./modifier') + +module.exports = class Transformation extends Modifier { + constructor ([regexp, format, flags]) { + super() + + this.regexp = new RegExp(regexp, flags.join('')) + this.format = format + } + + modify ([Construct, identifier]) { + class Transformation extends Construct { + constructor (identifier, { regexp, format }) { + super(identifier) + + this.regexp = regexp + this.format = format + + this.transform = (string, regexp, fold = sequence => sequence) => + 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 + }, '') + } + + deactivate (marker, buffer) { + const range = marker.getRange() + this.write(buffer, range, buffer.getTextInRange(range)) + } + + write (buffer, range, value) { + return buffer.setTextInRange(range, this.transform(value, this.regexp)) + } + + insert (buffer, position, value) { + return buffer.insert(position, this.transform(value, this.regexp)) + } + + toString () { + return this.transform(super.toString(), this.regexp) + } + } + + return new Transformation(identifier, this) + } +} diff --git a/lib/parser/config.js b/lib/parser/config.js new file mode 100644 index 00000000..4f47b114 --- /dev/null +++ b/lib/parser/config.js @@ -0,0 +1,13 @@ +module.exports = { + input: './lib/parser/snippet-body-parser.pegjs', + output: './lib/parser/snippet-body-parser.js', + dependencies: { + Snippet: '../constructs/snippet', + Tabstop: '../constructs/tabstop', + Variable: '../constructs/variable', + Choice: '../modifiers/choice', + Modifier: '../modifiers/modifier', + Placeholder: '../modifiers/placeholder', + Transformation: '../modifiers/transformation' + } +} diff --git a/lib/parser/snippet-body-parser.js b/lib/parser/snippet-body-parser.js new file mode 100644 index 00000000..22f2d522 --- /dev/null +++ b/lib/parser/snippet-body-parser.js @@ -0,0 +1,1723 @@ +// Generated by PEG.js v0.11.0-master.b7b87ea, https://pegjs.org/ + +"use strict"; + +var Snippet = require("../constructs/snippet"); +var Tabstop = require("../constructs/tabstop"); +var Variable = require("../constructs/variable"); +var Choice = require("../modifiers/choice"); +var Modifier = require("../modifiers/modifier"); +var Placeholder = require("../modifiers/placeholder"); +var Transformation = require("../modifiers/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 = "(?"; + var peg$c8 = ")"; + var peg$c9 = "\\E"; + var peg$c10 = "\\u"; + var peg$c11 = "\\l"; + var peg$c12 = "\\U"; + var peg$c13 = "\\L"; + var peg$c14 = "\\n"; + var peg$c15 = "\\r"; + var peg$c16 = "\\v"; + var peg$c17 = "\\t"; + var peg$c18 = "\\b"; + var peg$c19 = "\\f"; + var peg$c20 = "\\"; + var peg$c21 = "{"; + + var peg$r0 = /^[gimsuy]/; + var peg$r1 = /^[0-9]/; + var peg$r2 = /^[0-9a-f]/i; + + 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("(?", false); + var peg$e10 = peg$literalExpectation(")", false); + var peg$e11 = peg$literalExpectation("\\E", false); + var peg$e12 = peg$literalExpectation("\\u", false); + var peg$e13 = peg$literalExpectation("\\l", false); + var peg$e14 = peg$literalExpectation("\\U", false); + var peg$e15 = peg$literalExpectation("\\L", false); + var peg$e16 = peg$classExpectation([["0", "9"]], false, false); + var peg$e17 = peg$literalExpectation("\\n", false); + var peg$e18 = peg$literalExpectation("\\r", false); + var peg$e19 = peg$literalExpectation("\\v", false); + var peg$e20 = peg$literalExpectation("\\t", false); + var peg$e21 = peg$literalExpectation("\\b", false); + var peg$e22 = peg$literalExpectation("\\f", false); + var peg$e23 = peg$literalExpectation("\\", false); + var peg$e24 = peg$literalExpectation("{", false); + var peg$e25 = peg$classExpectation([["0", "9"], ["a", "f"]], false, true); + + var peg$f0 = function(body) { return new Snippet(body, legacySyntax) }; + var peg$f1 = function(construct) { return new Modifier().modify(identifier) }; + var peg$f2 = function(construct, modifier) { return modifier.modify(construct) }; + var peg$f3 = function(identifier) { return [Tabstop, identifier] }; + var peg$f4 = function(identifier) { return [Variable, identifier] }; + var peg$f5 = function(char) { return /[\p{L}\d_]/u.test(char) }; + var peg$f6 = function(choices) { return new Choice(choices) }; + var peg$f7 = function(content) { return new Placeholder(content) }; + var peg$f8 = function(transformation) { return new Transformation(transformation) }; + var peg$f9 = function() { return new Modifier() }; + var peg$f10 = function(first, rest) { return [first, ...rest] }; + var peg$f11 = function() { return escape('$,|') }; + var peg$f12 = function() { /*{*/ return escape('$}') }; + var peg$f13 = function() { return escapes.push('/') }; + var peg$f14 = function() { return escape('\\(/') }; + var peg$f15 = function() { return [''] }; + var peg$f16 = function(group, insert, replacement) { + return { group: `$${group || '$'}`, insert, replacement } + }; + var peg$f17 = function() { return escape(':)') }; + var peg$f18 = function() { return sequence => sequence }; + var peg$f19 = function() { return upperFirst }; + var peg$f20 = function() { return lowerFirst }; + var peg$f21 = function() { return toUpper }; + var peg$f22 = function() { return toLower }; + var peg$f23 = function(int) { return Number.parseInt(int) }; + var peg$f24 = function(char) { return !escaped(char) }; + var peg$f25 = function(chars) { return chars.join('') }; + var peg$f26 = function() { return escapes.pop() }; + var peg$f27 = function() { return '\n' }; + var peg$f28 = function() { return '\r' }; + var peg$f29 = function() { return '\v' }; + var peg$f30 = function() { return '\t' }; + var peg$f31 = function() { return '\b' }; + var peg$f32 = function() { return '\f' }; + var peg$f33 = function(codepoint) { + return String.fromCodePoint(Number.parseInt(codepoint, 16)) + }; + var peg$f34 = 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$parseConstruct(); + 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$parseConstruct(); + if (s2 !== peg$FAILED) { + s3 = peg$parseModifier(); + 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; + } + } + + return s0; + } + + function peg$parseConstruct() { + var s0, s1; + + var rule$expects = function (expected) { + if (peg$silentFails === 0) peg$expect(expected); + } + + s0 = peg$currPos; + s1 = peg$parseInt(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f3(s1); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parseVariable(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f4(s1); + } + s0 = s1; + } + + 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$parseModifier() { + var s0, s1; + + var rule$expects = function (expected) { + if (peg$silentFails === 0) peg$expect(expected); + } + + s0 = peg$currPos; + s1 = peg$parseChoice(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f6(s1); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parsePlaceholder(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f7(s1); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parseTransformation(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f8(s1); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = ''; + peg$savedPos = s0; + s1 = peg$f9(); + 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$f10(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$f11(); + if (s1) { + s1 = undefined; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + s2 = peg$parseSnippet(); + 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$f12(); + 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$f13(); + 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$f14(); + if (s1) { + s1 = undefined; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseConditionalInsert(); + if (s3 === peg$FAILED) { + s3 = peg$parseCaseFold(); + if (s3 === peg$FAILED) { + s3 = peg$parseString(); + } + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseConditionalInsert(); + if (s3 === peg$FAILED) { + s3 = peg$parseCaseFold(); + 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$f15(); + s0 = s1; + } + + return s0; + } + + function peg$parseConditionalInsert() { + var s0, s1, s2, s3, s4, s5; + + var rule$expects = function (expected) { + if (peg$silentFails === 0) peg$expect(expected); + } + + s0 = peg$currPos; + rule$expects(peg$e9); + if (input.substr(peg$currPos, 2) === peg$c7) { + s1 = peg$c7; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + s2 = peg$parseInt(); + if (s2 !== peg$FAILED) { + s3 = peg$parseInsert(); + if (s3 !== peg$FAILED) { + s4 = peg$parseInsert(); + if (s4 === peg$FAILED) { + s4 = null; + } + rule$expects(peg$e10); + if (input.charCodeAt(peg$currPos) === 41) { + s5 = peg$c8; + peg$currPos++; + } else { + s5 = peg$FAILED; + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f16(s2, s3, s4); + } 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$parseInsert() { + 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$f17(); + 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) { + 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$parseCaseFold() { + var s0, s1; + + var rule$expects = function (expected) { + if (peg$silentFails === 0) peg$expect(expected); + } + + s0 = peg$currPos; + rule$expects(peg$e11); + if (input.substr(peg$currPos, 2) === peg$c9) { + s1 = peg$c9; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f18(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + rule$expects(peg$e12); + if (input.substr(peg$currPos, 2) === peg$c10) { + s1 = peg$c10; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f19(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + rule$expects(peg$e13); + if (input.substr(peg$currPos, 2) === peg$c11) { + s1 = peg$c11; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f20(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + rule$expects(peg$e14); + if (input.substr(peg$currPos, 2) === peg$c12) { + s1 = peg$c12; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f21(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + rule$expects(peg$e15); + if (input.substr(peg$currPos, 2) === peg$c13) { + s1 = peg$c13; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f22(); + } + s0 = s1; + } + } + } + } + + 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$e16); + 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$e16); + 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$f23(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$f24(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$f24(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$f25(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$f26(); + 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$e17); + if (input.substr(peg$currPos, 2) === peg$c14) { + s1 = peg$c14; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f27(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + rule$expects(peg$e18); + if (input.substr(peg$currPos, 2) === peg$c15) { + s1 = peg$c15; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f28(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + rule$expects(peg$e19); + if (input.substr(peg$currPos, 2) === peg$c16) { + s1 = peg$c16; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f29(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + rule$expects(peg$e20); + if (input.substr(peg$currPos, 2) === peg$c17) { + s1 = peg$c17; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f30(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + rule$expects(peg$e21); + if (input.substr(peg$currPos, 2) === peg$c18) { + s1 = peg$c18; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f31(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + rule$expects(peg$e22); + if (input.substr(peg$currPos, 2) === peg$c19) { + s1 = peg$c19; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f32(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + rule$expects(peg$e12); + if (input.substr(peg$currPos, 2) === peg$c10) { + s1 = peg$c10; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + s2 = peg$parseUTF32(); + if (s2 === peg$FAILED) { + s2 = peg$parseUTF16(); + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f33(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$e23); + if (input.charCodeAt(peg$currPos) === 92) { + s1 = peg$c20; + 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$f34(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; + } + + function peg$parseUTF32() { + var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; + + var rule$expects = function (expected) { + if (peg$silentFails === 0) peg$expect(expected); + } + + s0 = peg$currPos; + rule$expects(peg$e24); + if (input.charCodeAt(peg$currPos) === 123) { + s1 = peg$c21; + peg$currPos++; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + s3 = peg$currPos; + s4 = peg$parseHex(); + if (s4 !== peg$FAILED) { + s5 = peg$parseHex(); + if (s5 === peg$FAILED) { + s5 = null; + } + s6 = peg$parseHex(); + if (s6 === peg$FAILED) { + s6 = null; + } + s7 = peg$parseHex(); + if (s7 === peg$FAILED) { + s7 = null; + } + s8 = peg$parseHex(); + if (s8 === peg$FAILED) { + s8 = null; + } + s9 = peg$parseHex(); + if (s9 === peg$FAILED) { + s9 = null; + } + s4 = [s4, s5, s6, s7, s8, s9]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + s2 = input.substring(s2, peg$currPos); + } else { + s2 = s3; + } + 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; + } + + return s0; + } + + function peg$parseUTF16() { + var s0, s1, s2, s3, s4, s5; + + var rule$expects = function (expected) { + if (peg$silentFails === 0) peg$expect(expected); + } + + s0 = peg$currPos; + s1 = peg$currPos; + s2 = peg$parseHex(); + if (s2 !== peg$FAILED) { + s3 = peg$parseHex(); + if (s3 !== peg$FAILED) { + s4 = peg$parseHex(); + if (s4 !== peg$FAILED) { + s5 = peg$parseHex(); + if (s5 !== peg$FAILED) { + s2 = [s2, s3, s4, s5]; + s1 = s2; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + s0 = input.substring(s0, peg$currPos); + } else { + s0 = s1; + } + + return s0; + } + + function peg$parseHex() { + var s0; + + var rule$expects = function (expected) { + if (peg$silentFails === 0) peg$expect(expected); + } + + rule$expects(peg$e25); + if (peg$r2.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + 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)) + const lowerFirst = string => string.replace(/^\p{CWU}/u, char => toLower(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) + // At some point phase out old textmate syntax + let legacySyntax = false + + + 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/lib/parser/snippet-body-parser.pegjs b/lib/parser/snippet-body-parser.pegjs new file mode 100644 index 00000000..9496341f --- /dev/null +++ b/lib/parser/snippet-body-parser.pegjs @@ -0,0 +1,91 @@ +{ + const toUpper = string => string.toLocaleUpperCase() + const toLower = string => string.toLocaleLowerCase() + const upperFirst = string => string.replace(/^\p{CWU}/u, char => toUpper(char)) + const lowerFirst = string => string.replace(/^\p{CWU}/u, char => toLower(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) + // At some point phase out old textmate syntax + let legacySyntax = false +} + +/* +Test recursion $1 $a ${2} ${b} ${c:d} ${e:f${g:h}i} ${j:k${l|m,n,o${p}q,r${s:t}u,v${w|y,x|}z|}} $å ${ä} ${ö|1,2,3|} +Test transforms ... +Test escape sequences ... +*/ + +Snippet = body:(Expression / String)+ { return new Snippet(body, legacySyntax) } + +Expression + = "$" construct:Construct { return new Modifier().modify(identifier) } + / "${" construct:Construct modifier:Modifier "}" { return modifier.modify(construct) } + +Construct + = identifier:Tabstop { return [Tabstop, identifier] } + / identifier:Variable { return [Variable, identifier] } + +Tabstop = Int + +Variable = $(char:. & { return /[\p{L}\d_]/u.test(char) })+ + +Modifier + = choices:Choice { return new Choice(choices) } + / content:Placeholder { return new Placeholder(content) } + / transformation:Transformation { return new Transformation(transformation) } + / "" { return new Modifier() } + +Choice = "|" first:Selection rest:("," @Selection)* "|" { return [first, ...rest] } + +Selection = & { return escape('$,|') } @Snippet EOL + +Placeholder = ":" & { /*{*/ return escape('$}') } @Snippet EOL + +Transformation = "/" @RegExp "/" @Format "/" @$[gimsuy]* + +RegExp = & { return escapes.push('/') } @String EOL + +Format + = & { return escape('\\(/') } @(ConditionalInsert / CaseFold / String)+ EOL + / "" { return [''] } + +ConditionalInsert + = "(?" group:Int insert:Insert replacement:Insert? ")" { + return { group: `$${group || '$'}`, insert, replacement } + } + +Insert = ":" & { return escape(':)') } @String EOL + +CaseFold + = "\\E" { return sequence => sequence } + / "\\u" { return upperFirst } + / "\\l" { return lowerFirst } + / "\\U" { return toUpper } + / "\\L" { return toLower } + +Int = int:$[0-9]+ { return Number.parseInt(int) } + +String = chars:(@Escape / @char:. & { return !escaped(char) })+ { return chars.join('') } + +EOL = & { return escapes.pop() } + +Escape + = "\\n" { return '\n' } + / "\\r" { return '\r' } + / "\\v" { return '\v' } + / "\\t" { return '\t' } + / "\\b" { return '\b' } + / "\\f" { return '\f' } + / "\\u" codepoint:(UTF32 / UTF16) { + return String.fromCodePoint(Number.parseInt(codepoint, 16)) + } + / "\\" @char:. & { return escaped(char) } + +UTF32 = "{" @$(Hex Hex? Hex? Hex? Hex? Hex?) "}" + +UTF16 = $(Hex Hex Hex Hex) + +Hex = [0-9a-f]i diff --git a/lib/snippet-body-parser.pegjs b/lib/snippet-body-parser.pegjs deleted file mode 100644 index e340e1ee..00000000 --- a/lib/snippet-body-parser.pegjs +++ /dev/null @@ -1,105 +0,0 @@ -{ - const toUpper = (string, locale = navigator.language) => string.toLocaleUpperCase(locale) - const toLower = (string, locale = navigator.language) => string.toLocaleLowerCase(locale) - const upperFirst = (string, locale = navigator.language) => - string.replace(/^\p{CWU}/u, character => toUpper(character, locale)) - const lowerFirst = (string, locale = navigator.language) => - string.replace(/^\p{CWU}/u, character => toLower(character, locale)) -} - -Snippet = body:(Text / Variable)* { return new Snippet({ body }) } - -Text = text:(@EscapeSequence / !Variable @.)+ { return text.join("") } - -Variable - = "$" variable:(Identifier / Expression) { - switch (true) { - case variable.body instanceof Set: - return new Choice(variable) - case Number.isInteger(variable.identifier): - return new Tabstop(variable) - default: - return new Variable(variable) - } - } - -Identifier - = identifier:(Integer / $(character:. & { return /[\p{L}\d_]/u.test(character) })+) { - return { identifier } - } - -Expression - = "{" - identifier:Identifier - expression:(& { return !identifier.identifier.length } @Choice / Placeholder / Transformation)? - "}" { return { ...identifier, ...expression } } - -Placeholder = ":" body:(Value / Variable)+ { return { body } } - -Value = text:(@EscapeSequence / !Variable @[^}])+ { return text.join("") } - -Choice = "|" choices:(@Selection ","?)+ "|" { return { body: new Set(choices) } } - -Selection = text:(@EscapeSequence / !Variable @[^,|}])+ { return text.join("") } - -Transformation - = "/" regexp:$[^/}]+ "/" format:Format "/" flags:$[gimsuy]* { - return { - transformation: (value) => { - let fold = (sequence) => sequence - regexp = new RegExp(regexp, flags) - return format.reduce((result, sequence) => { - if (sequence instanceof Function) { - fold = sequence - } else if (Array.isArray(sequence)) { - const [group, insertion, replacement = ""] = sequence - result += fold(value.replace(regexp, group) ? insertion : replacement) - } else { - result += fold(value.replace(regexp, sequence), value, regexp) - } - return result - }, "") - } - } - } - -Format - = (CaseFold / ConditionalInsert / Replacement)+ - / "" { return [""] } - -Replacement - = text:(@EscapeSequence / !CaseFold !ConditionalInsert @[^/}])+ { return text.join("") } - -CaseFold - = "\\E" { return sequence => sequence } - / "\\u" { return upperFirst } - / "\\l" { return lowerFirst } - / "\\U" { return toUpper } - / "\\L" { return toLower } - -ConditionalInsert - = "(?" group:Integer insertion:Insertion replacement:Insertion? ")" { - return [`$${group}`, insertion, replacement != null ? replacement : undefined] - } - -Insertion = ":" text:(@EscapeSequence / @[^:)])+ { return text.join("") } - -EscapeSequence - = "\\n" { return "\n" } - / "\\r" { return "\r" } - / "\\v" { return "\v" } - / "\\t" { return "\t" } - / "\\b" { return "\b" } - / "\\f" { return "\f" } - / "\\u" codepoint:(UTF16 / UTF32) { - return String.fromCodePoint(Number.parseInt(codepoint, 16)) - } - / "\\" @. - -UTF16 = $(HexDigit HexDigit HexDigit HexDigit) - -UTF32 = "{" @$(HexDigit HexDigit? HexDigit? HexDigit? HexDigit? HexDigit?) "}" - -HexDigit = [0-9a-f]i - -Integer = integer:$[0-9]+ { return Number.parseInt(integer) } diff --git a/lib/snippet.js b/lib/snippet.js deleted file mode 100644 index c44074f9..00000000 --- a/lib/snippet.js +++ /dev/null @@ -1,68 +0,0 @@ -const { CompositeDisposable } = require('atom') - -const Tabstop = require('./tabstop') - -module.exports = class Snippet extends Tabstop { - static getVariables (cursor) { - return {} - } - - constructor (variable) { - super(variable) - this.disposables = new CompositeDisposable() - this.stop = 0 - } - - destroy (cursor = atom.workspace.getActiveTextEditor().getLastCursor()) { - this.disposables.dispose() - // Normalize line endings (why isn't there a method for this?) - cursor.editor.buffer.setTextInRange(this.range, cursor.editor.buffer.getTextInRange(this.range)) - cursor.editor.normalizeTabsInBufferRange(this.range) - } - - expand (cursor, variables = {}) { - // Construct variables from given and global without given variables overriding global ones - variables = { ...variables, ...Snippet.getVariables(cursor) } - - const index = cursor.editor.buffer.characterIndexForPosition(cursor.getBufferPosition()) - - // Insert snippet text without normalizing line endings, so variabe ranges match - cursor.editor.buffer.insert(cursor.getBufferPosition(), this.toString(), { normalizeLineEndings: false }) - - // Generate tabstops and expanded text - this.stops = super.expand(cursor, variables, this.disposables, index) - // shift-tab could be implemented by defining a custom iterator / linked list - // Sorts stops in ascending order, leaving zero last - .sort(({ identifier: a }, { identifier: b }) => !a - !b || a - b) - // Include an ending tabstop if one not present - if (this.stops[this.stops.length - 1].identifier !== 0) { - this.stops.push({ range: [this.range.end, this.range.end] }) - } - - const target = 'atom-text-editor:not([mini])' - const next = `snippets:next-tab-stop-${this.identifier}` - - this.disposables.add( - atom.commands.add(target, next, event => - this.nextStop(cursor, event.originalEvent.shiftKey) || - event.abortKeyBinding()), - atom.keymaps.add(module.filename, { [target]: { tab: next } }), - cursor.onDidDestroy(() => this.destroy()), - cursor.onDidChangePosition(({ newBufferPosition }) => { - if (!this.range.containsPoint(newBufferPosition)) { - this.destroy(cursor) - } - })) - - this.nextStop(cursor) - return this - } - - nextStop (cursor) { - if (this.stops[this.stop]) { - cursor.selection.setBufferRange(this.stops[this.stop++].range) - return true - } - this.destroy() - } -} diff --git a/lib/snippets.js b/lib/snippets.js index d38c0533..8843b937 100644 --- a/lib/snippets.js +++ b/lib/snippets.js @@ -2,16 +2,10 @@ const { Emitter, CompositeDisposable, File } = require('atom') const CSON = require('season') const path = require('path') -const peg = require('pegjs') const fs = require('fs') const ScopedPropertyStore = require('scoped-property-store') -const Snippet = require('./snippet') -const Variable = require('./variable') -const Tabstop = require('./tabstop') -const Choice = require('./choice') - module.exports = class Snippets { static async activate () { this.subscriptions = new CompositeDisposable() @@ -35,7 +29,6 @@ module.exports = class Snippets { await Promise.all([ this.loadUserSnippets(), ...atom.packages.getActivePackages() - .sort(({ path }) => /\/node_modules\//.test(path) ? -1 : 1) .map(bundle => this.loadPackage(bundle)) ]) @@ -55,17 +48,10 @@ module.exports = class Snippets { this.subscriptions.dispose() } - static get bodyParser () { - delete this.bodyParser - const grammar = fs.readFileSync(path.join(module.filename, '../snippet-body-parser.pegjs'), 'utf8') - return (this.bodyParser = peg.generate(grammar, { - context: { - Snippet, - Variable, - Tabstop, - Choice - } - })) + static get parser () { + delete this.parser + + return (this.parser = require('./parser/snippet-body-parser.js')) } static getUserSnippetsPath () { @@ -110,11 +96,11 @@ module.exports = class Snippets { this.userSnippetsPath = this.getUserSnippetsPath() }))) } catch (error) { - console.warn(`Error loading snippets from '${this.userSnippetsPath}': ${error.stack != null ? error.stack : error}`) - atom.notifications.addWarning(`\ - Unable to load snippets from \`${this.userSnippetsPath}\`. - Make sure you have permissions to access the directory and file. - `, { detail: error.message, dismissable: true }) + atom.notifications.addWarning(`Unable to load snippets from: '${this.userSnippetsPath}'`, { + description: 'Make sure you have permissions to access the directory and file.', + detail: error.toString(), + dismissable: true + }) } } @@ -130,11 +116,11 @@ module.exports = class Snippets { ? this.packageDisposables.get(bundle).add(disposable) : this.packageDisposables.set(bundle, new CompositeDisposable(disposable)) } catch (error) { - console.warn(`Error loading snippets from '${snippetsFile}': ${error.stack != null ? error.stack : error}`) - atom.notifications.addWarning(`\ - Unable to load snippets from \`${snippetsFile}\`. - Make sure you have permissions to access the directory and file. - `, { detail: error.message, dismissable: true }) + atom.notifications.addWarning(`Unable to load snippets from: '${snippetsFile}'`, { + description: 'Make sure you have permissions to access the directory and file.', + detail: error.toString(), + dismissable: true + }) } }) } catch (error) { @@ -159,9 +145,9 @@ module.exports = class Snippets { static snippets () { return { - parse: string => this.bodyParser.parse(string), + parse: string => this.parser.parse(string), userSnippetsPath: () => this.userSnippetsPath, - snippets: () => this.snippetsByScopes, + snippetsByScopes: () => this.snippetsByScopes, loaded: () => this.loaded } } diff --git a/lib/tabstop-list.js b/lib/tabstop-list.js new file mode 100644 index 00000000..9353eb5a --- /dev/null +++ b/lib/tabstop-list.js @@ -0,0 +1,23 @@ +module.exports = class TabstopList extends Array { + constructor (markers) { + super() + + const unknowns = [] + + markers.forEach(marker => { + const { construct } = marker.getProperties() + + Number.isInteger(construct.identifier) + ? Array.isArray(this[construct.identifier]) + ? this[construct.identifier].push(marker.id) + : this[construct.identifier] = [marker.id] + : unknowns.push([marker.id]) + }) + // Include all unknown variables at the end + if (unknowns.length) { + this.push(...unknowns) + } + // Move 0th tabstop to last + this.push(this.shift()) + } +} diff --git a/lib/tabstop.js b/lib/tabstop.js deleted file mode 100644 index 9174752a..00000000 --- a/lib/tabstop.js +++ /dev/null @@ -1,33 +0,0 @@ -const { Range } = require('atom') - -const Variable = require('./variable') - -module.exports = class Tabstop extends Variable { - constructor (variable) { - super(variable) - - this.mirrors = [] - } - - expand (cursor, variables, disposables, offset) { - this.range = new Range(...[offset, offset + this.length] - .map(index => cursor.editor.buffer.positionForCharacterIndex(index))) - this.marker = cursor.editor.buffer.markRange(this.range) - - disposables.add({ dispose: () => { this.marker.destroy() } }) - - disposables.add(this.marker.onDidChange(({ textChanged, newTailPosition, newHeadPosition }) => { - this.range = new Range(newTailPosition, newHeadPosition) - if (textChanged) { - console.log(this.range) - console.log('"' + cursor.editor.buffer.getTextInRange(this.range) + '"') - console.log(this.identifier) - this.mirrors.forEach(mirror => { - console.log(this.identifier, mirror) - }) - } - })) - - return super.expand(cursor, variables, disposables, offset) - } -} diff --git a/lib/variable.js b/lib/variable.js deleted file mode 100644 index 85777d83..00000000 --- a/lib/variable.js +++ /dev/null @@ -1,40 +0,0 @@ -module.exports = class Variable { - constructor ({ identifier, body = [''], transformation }) { - this.identifier = identifier - this.body = body - this.transformation = transformation - - this.length = this.body.reduce((length, value) => length + value.length, 0) - } - - transform (value) { - return this.transformation - ? this.transformation(value) - : value - } - - expand (cursor, variables, disposables, offset) { - if (this.identifier in variables) { - this.body = this.transform(variables[this.identifier]) - this.length = this.body.length - offset += this.length - return this.body - } - return Array.isArray(this.body) - ? this.body.flatMap(value => { - if (value instanceof Variable) { - const expansion = value.expand(cursor, variables, disposables, offset) - offset += value.length - return [value, ...expansion] - } - offset += value.length - return [] - }) : '' - } - - toString () { - return Array.isArray(this.body) - ? this.body.reduce((string, value) => string + value, '') - : this.body || '' - } -} diff --git a/package-lock.json b/package-lock.json index 248e0a37..fb62f02d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1714,7 +1714,8 @@ "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==" + "integrity": "sha512-fwjzNiYHRUEUe/86Aaslb/ocbbsAupOcsJz+dlPYtgp3feCDRQOLChHO924XGh7fzSJBTdFCQTzmSOQaWjCTew==", + "dev": true }, "pify": { "version": "2.3.0", diff --git a/package.json b/package.json index 0ed0a5d6..c9d84758 100644 --- a/package.json +++ b/package.json @@ -8,20 +8,22 @@ "scripts": { "lint": "standard", "lint:fix": "npm run lint -- --fix", - "test": "npm run lint:fix && atom --test spec" + "test": "npm run lint:fix && atom --test spec", + "generate": "pegjs -c ./lib/parser/config.js" }, "dependencies": { "atom-select-list": "^0.7.0", - "pegjs": "^0.11.0-master.b7b87ea", "scoped-property-store": "^0.17.0", "season": "^6.0.2" }, "devDependencies": { + "pegjs": "^0.11.0-master.b7b87ea", "standard": "^14.3.4", "temp": "^0.9.1" }, "engines": { - "atom": "*" + "atom": ">=1.47.0", + "node": ">=12.0.0" }, "providedServices": { "snippets-dev": { From 062e7caafb60423fdd0562e90fa3b516d01eba77 Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Tue, 15 Sep 2020 21:31:53 +0300 Subject: [PATCH 13/17] implement choices and some fixes choices are now iterated over using shift-tab downside is that available choices aren't visible alternative could be inserting all chioces, and setting cursor selection to the currently choces one the downside being extremely long choices that could expand past the screen --- lib/constructs/construct.js | 31 +-- lib/constructs/snippet.js | 178 +++++++------- lib/constructs/tabstop.js | 5 +- lib/constructs/variable.js | 7 +- lib/modifiers/choice.js | 61 +++-- lib/modifiers/modifier.js | 7 +- lib/modifiers/placeholder.js | 14 +- lib/modifiers/transformation.js | 27 +-- lib/parser/snippet-body-parser.js | 5 +- lib/parser/snippet-body-parser.pegjs | 5 +- lib/snippets.js | 4 +- lib/tabstop-list.js | 23 -- package-lock.json | 337 +++++++++++---------------- package.json | 1 - 14 files changed, 319 insertions(+), 386 deletions(-) delete mode 100644 lib/tabstop-list.js diff --git a/lib/constructs/construct.js b/lib/constructs/construct.js index 3ee69482..473d11ab 100644 --- a/lib/constructs/construct.js +++ b/lib/constructs/construct.js @@ -3,24 +3,27 @@ module.exports = class Construct { this.identifier = identifier } - write (buffer, range, value) { - return buffer.setTextInRange(range, value) + insert (cursor, value) { + return cursor.editor.getBuffer().insert(cursor.getBufferPosition(), value) } - insert (buffer, position, value) { - return buffer.insert(position, value) + activate (mirror, cursor, stop) { + if (mirror === stop) { + cursor.selection.setBufferRange(stop.getRange()) + const subscription = cursor.onDidChangePosition(({ newBufferPosition }) => { + if (!stop.getRange().containsPoint(newBufferPosition)) { + stop.destroy() + subscription.dispose() + } + }) + } else { + cursor.editor.decorateMarker(mirror, { type: 'highlight' }) + stop.onDidDestroy(() => mirror.destroy()) + } } - deactivate (marker, buffer) { - marker.destroy() - } - - activate (marker, cursor) { - cursor.selection.setBufferRange(marker.getRange()) - } - - mark ({ layer, start, end = start, exclusive = true, construct = this }) { - layer.markRange({ start, end }, { exclusive }).setProperties({ construct }) + mark ({ tabstops, start, end = start, exclusive = true, construct = this }) { + tabstops.markRange({ start, end }, { exclusive }).setProperties({ construct }) } toString () { diff --git a/lib/constructs/snippet.js b/lib/constructs/snippet.js index 8073e5f7..204fb6e1 100644 --- a/lib/constructs/snippet.js +++ b/lib/constructs/snippet.js @@ -1,14 +1,12 @@ const { CompositeDisposable } = require('atom') const path = require('path') -const TabstopList = require('../tabstop-list') const Construct = require('./construct') module.exports = class Snippet extends Construct { - static getVariables (buffer, cursor, variables) { + static getVariables (cursor) { // Lazy getters for each variable return { - ...variables, // The currently selected text or the empty string get TM_SELECTED_TEXT () { delete this.TM_SELECTED_TEXT @@ -22,7 +20,7 @@ module.exports = class Snippet extends Construct { // The contents of the word under cursor or the empty string get TM_CURRENT_WORD () { delete this.TM_CURRENT_WORD - return (this.TM_CURRENT_WORD = buffer.getTextInRange(cursor.getCurrentWordBufferRange())) + return (this.TM_CURRENT_WORD = cursor.editor.getTextInBufferRange(cursor.getCurrentWordBufferRange())) }, // The zero-index based line number get TM_LINE_INDEX () { @@ -38,23 +36,23 @@ module.exports = class Snippet extends Construct { // The filename of the current document get TM_FILENAME () { delete this.TM_FILENAME - return (this.TM_FILENAME = path.basename(buffer.getPath())) + return (this.TM_FILENAME = cursor.editor.getTitle()) }, // The filename of the current document without its extensions get TM_FILENAME_BASE () { delete this.TM_FILENAME_BASE - const filepath = buffer.getPath() + const filepath = cursor.editor.getTitle() return (this.TM_FILENAME_BASE = path.basename(filepath, path.extname(filepath))) }, // The directory of the current document get TM_DIRECTORY () { delete this.TM_DIRECTORY - return (this.TM_DIRECTORY = path.dirname(buffer.getPath())) + return (this.TM_DIRECTORY = path.dirname(cursor.editor.getPath())) }, // The full file path of the current document get TM_FILEPATH () { delete this.TM_FILEPATH - return (this.TM_FILEPATH = buffer.getPath()) + return (this.TM_FILEPATH = cursor.editor.getPath()) }, // The contents of the clipboard get CLIPBOARD () { @@ -64,7 +62,7 @@ module.exports = class Snippet extends Construct { // The name of the opened workspace or folder get WORKSPACE_NAME () { delete this.WORKSPACE_NAME - const [projectPath] = atom.project.relativizePath(buffer.getPath()) + const [projectPath] = atom.project.relativizePath(cursor.editor.getPath()) return (this.WORKSPACE_NAME = path.basename(projectPath)) }, // Insert the current date and time @@ -138,90 +136,32 @@ module.exports = class Snippet extends Construct { } } - // we use static (for memory reasons and)? to enforce that snippets don't alter - // their own internal state once created (so they can be pre-generated and reused) - static expand (snippet, buffer, cursor, variables, editor) { - const disposables = new CompositeDisposable() - - // Construct variables from given and global - Object.defineProperties(variables, Object.getOwnPropertyDescriptors(Snippet.getVariables(buffer, cursor))) + static getTabstops (markers) { + const tabstops = [] + const unknowns = [] - // Create a new layer to store created tabstops - const layer = buffer.addMarkerLayer({ role: 'tabstops' }) + markers.forEach(marker => { + const { construct } = marker.getProperties() - disposables.add({ dispose: () => layer.destroy() }) - - // Define a marker that spans the whole snippet - // This will also be used as the ending tabstop if there isn't an explicit one - // Unlike all other tabstops, this marker isn't exclusive, meaning it expands - // with the inserted snippet bellow - snippet.mark({ layer, start: cursor.getBufferPosition(), exclusive: false }) - const [marker] = layer.getMarkers() - - snippet.body.forEach(value => { - value instanceof Object - ? value.expand(buffer, cursor, layer, variables) - : snippet.insert(buffer, cursor.getBufferPosition(), value) + Number.isInteger(construct.identifier) + ? Array.isArray(tabstops[construct.identifier]) + ? tabstops[construct.identifier].push(marker) + : tabstops[construct.identifier] = [marker] + : unknowns.push([marker]) }) - - // Only create tabstop stuff if we have any - if (layer.getMarkerCount() > 1) { - // The underlying data-structure is an Object, so the markers aren't guaranteed - // to be in insertion order. in reality they will be, as the ids used are integers - const tabstops = new TabstopList(layer.getMarkers()).entries() - - const nextStop = () => { - const next = tabstops.next() - if (next.done) { - return disposables.dispose() - } - - const { value: [id, [stopId, ...mirrorIds] = []] } = next - - const previous = tabstops[id - 1] || [] - - previous.forEach(stopId => { - const stop = layer.getMarker(stopId) - stop.getProperties().construct.deactivate(stop, buffer) - }) - - const stop = layer.getMarker(stopId) - const { construct } = stop.getProperties() - - construct.activate(stop, cursor) - mirrorIds.forEach(mirrorId => - editor.decorateMarker(layer.getMarker(mirrorId), { type: 'highlight' })) - - return true - } - - const target = 'atom-text-editor:not([mini])' - const next = `snippets:next-tab-stop-${layer.id}` - - disposables.add( - atom.keymaps.add(module.filename, { [target]: { tab: next } }), - atom.commands.add(target, next, event => nextStop() || - event.abortKeyBinding())) - - // Go to the first tabstop - nextStop() + // Include all unknown variables at the end + if (unknowns.length) { + tabstops.push(...unknowns) } + // Move 0th tabstop to last + tabstops.push(tabstops.shift()) - disposables.add( - cursor.onDidDestroy(() => disposables.dispose()), - cursor.onDidChangePosition(({ newBufferPosition }) => { - // Exclude endpoints, so that end tabstops don't trigger mirror logic - if (!marker.getRange().containsPoint(newBufferPosition, true)) { - disposables.dispose() - } - })) - - return marker.getRange() + return tabstops } constructor (body, legacySyntax) { // This snippet will work as the default ending tabstop - super(0) + super(0, false) this.body = body @@ -230,11 +170,9 @@ module.exports = class Snippet extends Construct { // helper cause Snippet isn't really ever available expand ({ - buffer = atom.workspace.getActiveTextEditor().getBuffer(), - cursor = atom.workspace.getActiveTextEditor().getLastCursor(), variables = {}, - // We absolutely need the same editor the cursor belongs to - editor = cursor.editor + cursor = atom.workspace.getActiveTextEditor().getLastCursor(), + tabstops = cursor.editor.getBuffer().addMarkerLayer({ role: 'tabstops' }) } = {}) { if (this.legacySyntax) { atom.notifications.addWarning('Snippets: Snippet uses deprecated syntax.', { @@ -243,7 +181,69 @@ module.exports = class Snippet extends Construct { }) } - return Snippet.expand(this, buffer, cursor, variables, editor) + // Construct variables from given and global + Object.defineProperties(variables, + Object.getOwnPropertyDescriptors(Snippet.getVariables(cursor))) + + const disposables = new CompositeDisposable() + + // 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 + disposables.add( + cursor.onDidDestroy(() => disposables.dispose()), + cursor.onDidChangePosition(({ newBufferPosition }) => { + // Exclude endpoints, so that end tabstops don't trigger mirror logic + if (!marker.getRange().containsPoint(newBufferPosition, true)) { + disposables.dispose() + } + })) + + // We are the outmost snippet + const parentSnippet = tabstops.getMarkerCount() === 1 + + this.body.forEach(value => { + value instanceof Object + ? value.expand(cursor, tabstops, variables) + : this.insert(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 = { + tabstops: Snippet.getTabstops(tabstops.getMarkers()), + get iterator () { + delete this.iterator + return (this.iterator = this.tabstops.values()) + }, + next () { + const { done, value: [stop, ...mirrors] = [] } = this.iterator.next() + return done + ? disposables.dispose() + // Cheaty way of returning true concisely + : [stop, ...mirrors].every(mirror => !stop.getProperties() + .construct.activate(mirror, cursor, stop)) + } + } + + 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.getRange() } // We work as the default ending tabstop, this is a special case diff --git a/lib/constructs/tabstop.js b/lib/constructs/tabstop.js index bbed3c90..9c37b7d7 100644 --- a/lib/constructs/tabstop.js +++ b/lib/constructs/tabstop.js @@ -1,8 +1,7 @@ const Construct = require('./construct') module.exports = class Tabstop extends Construct { - expand (buffer, cursor, layer, variables) { - const start = cursor.getBufferPosition() - this.mark({ layer, start }) + expand (cursor, tabstops, variables) { + this.mark({ tabstops, start: cursor.getBufferPosition() }) } } diff --git a/lib/constructs/variable.js b/lib/constructs/variable.js index a017b7eb..2daf1eaa 100644 --- a/lib/constructs/variable.js +++ b/lib/constructs/variable.js @@ -1,10 +1,9 @@ const Construct = require('./construct') module.exports = class Variable extends Construct { - expand (buffer, cursor, layer, variables) { - const position = cursor.getBufferPosition() + expand (cursor, tabstops, variables) { this.identifier in variables - ? this.insert(buffer, position, variables[this.identifier]) - : this.mark({ layer, ...this.insert(buffer, position, this.identifier) }) + ? this.insert(cursor, variables[this.identifier]) + : this.mark({ tabstops, ...this.insert(cursor, this.identifier) }) } } diff --git a/lib/modifiers/choice.js b/lib/modifiers/choice.js index 1cd255e2..7c350ad0 100644 --- a/lib/modifiers/choice.js +++ b/lib/modifiers/choice.js @@ -1,33 +1,60 @@ +const { CompositeDisposable } = require('atom') + const Modifier = require('./modifier') module.exports = class Choice extends Modifier { - constructor ([first, ...rest]) { + constructor (choices) { super() - this.default = first - this.choices = rest + this.choices = choices } - modify ([Construct, identifier]) { + create ([Construct, ...args]) { class Choice extends Construct { - constructor (identifier, { choices }) { - super(identifier) + constructor ({ choices: [first, ...rest] }, ...args) { + super(...args) - this.choices = choices + this.default = first + // Move the default last, so choice cycling works as expected + this.choices = [...rest, first] } - activate (marker, cursor) { - cursor.selection.setBufferRange(marker.getRange()) - // todo dropdownlist + activate (marker, cursor, mirror, target, command) { + // Don't bother if a mirror, the marker won't get iterated over + if (!mirror) { + const disposables = new CompositeDisposable() + + const cycle = `snippets:next-choice-${marker.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 + cursor.selection.insertText(value, { select: true }) + } + } + + disposables.add( + atom.keymaps.add(module.filename, { [target]: { 'shift-tab': cycle } }), + atom.commands.add(target, cycle, event => choices.next()), + cursor.onDidChangePosition(({ newBufferPosition }) => { + if (!marker.getRange().containsPoint(newBufferPosition)) { + disposables.dispose() + } + })) + } + return super.activate(marker, cursor, mirror, target, command) } - expand (buffer, cursor, layer, variables) { - if (this.identifier in variables && !variables[this.identifier]) { - const position = cursor.getBufferPosition() - layer.markRange(this.insert(buffer, position, this.default)) - .setProperties({ construct: this }) + expand (cursor, tabstops, variables) { + if (!(this.identifier in variables)) { + this.mark({ tabstops, ...this.insert(cursor, this.default) }) } else { - super.expand(buffer, cursor, layer, variables) + super.expand(cursor, tabstops, variables) } } @@ -36,6 +63,6 @@ module.exports = class Choice extends Modifier { } } - return new Choice(identifier, this) + return new Choice(this, ...args) } } diff --git a/lib/modifiers/modifier.js b/lib/modifiers/modifier.js index f004492a..f41ae4b1 100644 --- a/lib/modifiers/modifier.js +++ b/lib/modifiers/modifier.js @@ -1,7 +1,6 @@ module.exports = class Modifier { - modify ([Construct, identifier]) { - class Modifier extends Construct {} - - return new Modifier(identifier) + create ([Construct, ...args]) { + // Don't actually modify the Construct + return new Construct(...args) } } diff --git a/lib/modifiers/placeholder.js b/lib/modifiers/placeholder.js index 8943b671..46a14f3e 100644 --- a/lib/modifiers/placeholder.js +++ b/lib/modifiers/placeholder.js @@ -9,19 +9,19 @@ module.exports = class Placeholder extends Modifier { this.snippet.legacySyntax = false } - modify ([Construct, identifier]) { + create ([Construct, ...args]) { class Placeholder extends Construct { - constructor (identifier, { snippet }) { - super(identifier) + constructor ({ snippet }, ...args) { + super(...args) this.snippet = snippet } - expand (buffer, cursor, layer, variables) { + expand (cursor, tabstops, variables) { if (!(this.identifier in variables)) { - this.mark({ layer, ...this.snippet.expand({ buffer, cursor, variables }) }) + this.mark({ tabstops, ...this.snippet.expand({ cursor, tabstops, variables }) }) } else { - super.expand(buffer, cursor, layer, variables) + super.expand(cursor, tabstops, variables) } } @@ -30,6 +30,6 @@ module.exports = class Placeholder extends Modifier { } } - return new Placeholder(identifier, this) + return new Placeholder(this, ...args) } } diff --git a/lib/modifiers/transformation.js b/lib/modifiers/transformation.js index 7d2e940d..b8becc8d 100644 --- a/lib/modifiers/transformation.js +++ b/lib/modifiers/transformation.js @@ -8,10 +8,10 @@ module.exports = class Transformation extends Modifier { this.format = format } - modify ([Construct, identifier]) { + create ([Construct, ...args]) { class Transformation extends Construct { - constructor (identifier, { regexp, format }) { - super(identifier) + constructor ({ regexp, format }, ...args) { + super(...args) this.regexp = regexp this.format = format @@ -28,17 +28,18 @@ module.exports = class Transformation extends Modifier { }, '') } - deactivate (marker, buffer) { - const range = marker.getRange() - this.write(buffer, range, buffer.getTextInRange(range)) + activate (mirror, cursor, stops) { + mirror.onDidDestroy(() => { + const range = mirror.getRange() + const buffer = cursor.editor.getBuffer() + const text = this.transform(buffer.getTextInRange(range), this.regexp) + buffer.setTextInRange(range, text) + }) + super.activate(mirror, cursor, stops) } - write (buffer, range, value) { - return buffer.setTextInRange(range, this.transform(value, this.regexp)) - } - - insert (buffer, position, value) { - return buffer.insert(position, this.transform(value, this.regexp)) + insert (cursor, value) { + return super.insert(cursor, this.transform(value, this.regexp)) } toString () { @@ -46,6 +47,6 @@ module.exports = class Transformation extends Modifier { } } - return new Transformation(identifier, this) + return new Transformation(this, ...args) } } diff --git a/lib/parser/snippet-body-parser.js b/lib/parser/snippet-body-parser.js index 22f2d522..d2dab08f 100644 --- a/lib/parser/snippet-body-parser.js +++ b/lib/parser/snippet-body-parser.js @@ -198,8 +198,8 @@ function peg$parse(input, options) { var peg$e25 = peg$classExpectation([["0", "9"], ["a", "f"]], false, true); var peg$f0 = function(body) { return new Snippet(body, legacySyntax) }; - var peg$f1 = function(construct) { return new Modifier().modify(identifier) }; - var peg$f2 = function(construct, modifier) { return modifier.modify(construct) }; + var peg$f1 = function(construct) { return new Modifier().create(construct) }; + var peg$f2 = function(construct, modifier) { return modifier.create(construct) }; var peg$f3 = function(identifier) { return [Tabstop, identifier] }; var peg$f4 = function(identifier) { return [Variable, identifier] }; var peg$f5 = function(char) { return /[\p{L}\d_]/u.test(char) }; @@ -1699,6 +1699,7 @@ function peg$parse(input, options) { const escapes = ['$'] const escape = iterable => escapes.push(iterable) const escaped = value => escapes[escapes.length - 1].includes(value) + // At some point phase out old textmate syntax let legacySyntax = false diff --git a/lib/parser/snippet-body-parser.pegjs b/lib/parser/snippet-body-parser.pegjs index 9496341f..05ea2af2 100644 --- a/lib/parser/snippet-body-parser.pegjs +++ b/lib/parser/snippet-body-parser.pegjs @@ -8,6 +8,7 @@ const escapes = ['$'] const escape = iterable => escapes.push(iterable) const escaped = value => escapes[escapes.length - 1].includes(value) + // At some point phase out old textmate syntax let legacySyntax = false } @@ -21,8 +22,8 @@ Test escape sequences ... Snippet = body:(Expression / String)+ { return new Snippet(body, legacySyntax) } Expression - = "$" construct:Construct { return new Modifier().modify(identifier) } - / "${" construct:Construct modifier:Modifier "}" { return modifier.modify(construct) } + = "$" construct:Construct { return new Modifier().create(construct) } + / "${" construct:Construct modifier:Modifier "}" { return modifier.create(construct) } Construct = identifier:Tabstop { return [Tabstop, identifier] } diff --git a/lib/snippets.js b/lib/snippets.js index 8843b937..9b5d65a6 100644 --- a/lib/snippets.js +++ b/lib/snippets.js @@ -70,9 +70,9 @@ module.exports = class Snippets { return (this.userSnippetsPath = this.getUserSnippetsPath()) } - static async loadSnippetsFile (filepath) { + static loadSnippetsFile (filepath) { const priority = filepath === this.userSnippetsPath ? 1000 : 0 - return await new Promise((resolve, reject) => + return new Promise((resolve, reject) => CSON.readFile(filepath, (error, object) => error == null ? resolve(this.snippetsByScopes.addProperties(filepath, object, { priority })) : reject(error))) diff --git a/lib/tabstop-list.js b/lib/tabstop-list.js deleted file mode 100644 index 9353eb5a..00000000 --- a/lib/tabstop-list.js +++ /dev/null @@ -1,23 +0,0 @@ -module.exports = class TabstopList extends Array { - constructor (markers) { - super() - - const unknowns = [] - - markers.forEach(marker => { - const { construct } = marker.getProperties() - - Number.isInteger(construct.identifier) - ? Array.isArray(this[construct.identifier]) - ? this[construct.identifier].push(marker.id) - : this[construct.identifier] = [marker.id] - : unknowns.push([marker.id]) - }) - // Include all unknown variables at the end - if (unknowns.length) { - this.push(...unknowns) - } - // Move 0th tabstop to last - this.push(this.shift()) - } -} diff --git a/package-lock.json b/package-lock.json index fb62f02d..4f2d1751 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,21 +37,21 @@ "dev": true }, "acorn": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", - "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", + "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", "dev": true }, "acorn-jsx": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", - "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "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.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "version": "6.12.5", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", + "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -117,14 +117,10 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, - "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==", - "requires": { - "etch": "^0.12.6", - "fuzzaldrin": "^2.1.0" - } + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" }, "atom-slick": { "version": "2.0.0", @@ -137,9 +133,9 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "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" @@ -520,12 +516,6 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "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 - }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -534,12 +524,6 @@ "requires": { "ansi-regex": "^4.1.0" } - }, - "strip-json-comments": { - "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 } } }, @@ -579,15 +563,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } } } }, @@ -679,15 +654,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } } } }, @@ -710,15 +676,6 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } } } }, @@ -753,15 +710,6 @@ "requires": { "esutils": "^2.0.2" } - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } } } }, @@ -772,12 +720,12 @@ "dev": true }, "eslint-scope": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", - "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "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.1.0", + "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, @@ -823,20 +771,28 @@ }, "dependencies": { "estraverse": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", - "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "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.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "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": "^4.1.0" + "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": { @@ -851,11 +807,6 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "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", @@ -873,9 +824,9 @@ }, "dependencies": { "type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", - "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", + "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" } } }, @@ -952,20 +903,6 @@ "write": "1.0.3" }, "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "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" - } - }, "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -992,13 +929,6 @@ "mkdirp": "^0.5.1", "rimraf": "^2.5.2", "underscore-plus": "1.x" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" - } } }, "fs.realpath": { @@ -1018,11 +948,6 @@ "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-stdin": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", @@ -1030,10 +955,9 @@ "dev": true }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", - "dev": true, + "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", @@ -1111,6 +1035,12 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "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.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -1137,14 +1067,14 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "inquirer": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.2.tgz", - "integrity": "sha512-DF4osh1FM6l0RJc5YWYhSDB6TawiBRlbV9Cox8MWlidU218Tb7fm3lQTULyUJDfJ0tjbzl0W4q651mrCCEM55w==", + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", @@ -1153,7 +1083,7 @@ "cli-width": "^3.0.0", "external-editor": "^3.0.3", "figures": "^3.0.0", - "lodash": "^4.17.16", + "lodash": "^4.17.19", "mute-stream": "0.0.8", "run-async": "^2.4.0", "rxjs": "^6.6.0", @@ -1236,9 +1166,9 @@ } }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "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" @@ -1258,9 +1188,9 @@ "dev": true }, "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz", + "integrity": "sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==", "dev": true }, "is-date-object": { @@ -1292,10 +1222,16 @@ "is-extglob": "^2.1.1" } }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "dev": true + }, "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "dev": true, "requires": { "has-symbols": "^1.0.1" @@ -1418,9 +1354,9 @@ } }, "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "dev": true }, "loose-envify": { @@ -1505,15 +1441,6 @@ "validate-npm-package-license": "^3.0.1" }, "dependencies": { - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -1546,15 +1473,37 @@ "dev": true }, "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz", + "integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } } }, "object.entries": { @@ -1601,9 +1550,9 @@ } }, "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { "mimic-fn": "^2.1.0" @@ -1625,7 +1574,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "requires": { "lcid": "^1.0.0" @@ -1904,6 +1853,15 @@ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1926,21 +1884,6 @@ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "requires": { "glob": "^7.1.3" - }, - "dependencies": { - "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" - } - } } }, "run-async": { @@ -1956,9 +1899,9 @@ "dev": true }, "rxjs": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", - "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -2103,14 +2046,6 @@ "get-stdin": "^7.0.0", "minimist": "^1.2.5", "pkg-conf": "^3.1.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - } } }, "string-width": { @@ -2145,7 +2080,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -2157,6 +2092,12 @@ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, + "strip-json-comments": { + "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", @@ -2227,20 +2168,6 @@ "rimraf": "~2.6.2" }, "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "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" - } - }, "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -2300,9 +2227,9 @@ "dev": true }, "underscore": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", - "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==" + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.11.0.tgz", + "integrity": "sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw==" }, "underscore-plus": { "version": "1.7.0", @@ -2319,9 +2246,9 @@ "dev": true }, "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -2365,7 +2292,7 @@ }, "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", @@ -2399,7 +2326,7 @@ }, "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", diff --git a/package.json b/package.json index c9d84758..3ad6c4a4 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,6 @@ "generate": "pegjs -c ./lib/parser/config.js" }, "dependencies": { - "atom-select-list": "^0.7.0", "scoped-property-store": "^0.17.0", "season": "^6.0.2" }, From 06a627c2eccd63da3bcafda6704d1d35bf64c0c9 Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Sat, 26 Sep 2020 16:30:05 +0300 Subject: [PATCH 14/17] more temp stuff --- {lib/constructs => constructs}/construct.js | 0 {lib/constructs => constructs}/snippet.js | 4 +- {lib/constructs => constructs}/tabstop.js | 0 {lib/constructs => constructs}/variable.js | 0 {lib/modifiers => modifiers}/choice.js | 0 {lib/modifiers => modifiers}/modifier.js | 0 {lib/modifiers => modifiers}/placeholder.js | 0 .../modifiers => modifiers}/transformation.js | 0 package-lock.json | 53 +++++----- package.json | 11 ++- {lib/parser => parser}/config.js | 4 +- {lib/parser => parser}/snippet-body-parser.js | 0 .../snippet-body-parser.pegjs | 0 ...pets-available.js => snippets-available.js | 0 lib/snippets.js => snippets.js | 97 +++++++------------ spec/snippet-spec.js | 0 specs/snippets-spec.js | 64 ++++++++++++ 17 files changed, 139 insertions(+), 94 deletions(-) rename {lib/constructs => constructs}/construct.js (100%) rename {lib/constructs => constructs}/snippet.js (98%) rename {lib/constructs => constructs}/tabstop.js (100%) rename {lib/constructs => constructs}/variable.js (100%) rename {lib/modifiers => modifiers}/choice.js (100%) rename {lib/modifiers => modifiers}/modifier.js (100%) rename {lib/modifiers => modifiers}/placeholder.js (100%) rename {lib/modifiers => modifiers}/transformation.js (100%) rename {lib/parser => parser}/config.js (76%) rename {lib/parser => parser}/snippet-body-parser.js (100%) rename {lib/parser => parser}/snippet-body-parser.pegjs (100%) rename lib/snippets-available.js => snippets-available.js (100%) rename lib/snippets.js => snippets.js (62%) delete mode 100644 spec/snippet-spec.js create mode 100644 specs/snippets-spec.js diff --git a/lib/constructs/construct.js b/constructs/construct.js similarity index 100% rename from lib/constructs/construct.js rename to constructs/construct.js diff --git a/lib/constructs/snippet.js b/constructs/snippet.js similarity index 98% rename from lib/constructs/snippet.js rename to constructs/snippet.js index 204fb6e1..d99f6db9 100644 --- a/lib/constructs/snippet.js +++ b/constructs/snippet.js @@ -170,9 +170,9 @@ module.exports = class Snippet extends Construct { // helper cause Snippet isn't really ever available expand ({ - variables = {}, cursor = atom.workspace.getActiveTextEditor().getLastCursor(), - tabstops = cursor.editor.getBuffer().addMarkerLayer({ role: 'tabstops' }) + tabstops = cursor.editor.addMarkerLayer(), + variables = {} } = {}) { if (this.legacySyntax) { atom.notifications.addWarning('Snippets: Snippet uses deprecated syntax.', { diff --git a/lib/constructs/tabstop.js b/constructs/tabstop.js similarity index 100% rename from lib/constructs/tabstop.js rename to constructs/tabstop.js diff --git a/lib/constructs/variable.js b/constructs/variable.js similarity index 100% rename from lib/constructs/variable.js rename to constructs/variable.js diff --git a/lib/modifiers/choice.js b/modifiers/choice.js similarity index 100% rename from lib/modifiers/choice.js rename to modifiers/choice.js diff --git a/lib/modifiers/modifier.js b/modifiers/modifier.js similarity index 100% rename from lib/modifiers/modifier.js rename to modifiers/modifier.js diff --git a/lib/modifiers/placeholder.js b/modifiers/placeholder.js similarity index 100% rename from lib/modifiers/placeholder.js rename to modifiers/placeholder.js diff --git a/lib/modifiers/transformation.js b/modifiers/transformation.js similarity index 100% rename from lib/modifiers/transformation.js rename to modifiers/transformation.js diff --git a/package-lock.json b/package-lock.json index 4f2d1751..a4a07d91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -122,6 +122,15 @@ "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==", + "requires": { + "etch": "^0.12.6", + "fuzzaldrin": "^2.1.0" + } + }, "atom-slick": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/atom-slick/-/atom-slick-2.0.0.tgz", @@ -807,6 +816,11 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "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", @@ -948,6 +962,11 @@ "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-stdin": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", @@ -1400,6 +1419,12 @@ "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", @@ -1574,7 +1599,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "requires": { "lcid": "^1.0.0" @@ -2080,7 +2105,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -2159,26 +2184,6 @@ } } }, - "temp": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.1.tgz", - "integrity": "sha512-WMuOgiua1xb5R56lE0eH6ivpVmg/lq2OHm4+LtT/xtEtPQ+sz6N3bBM6WZ5FvO1lO4IKIOb43qnhoc4qxP5OeA==", - "dev": true, - "requires": { - "rimraf": "~2.6.2" - }, - "dependencies": { - "rimraf": { - "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.1.3" - } - } - } - }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -2292,7 +2297,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { "string-width": "^1.0.1", @@ -2326,7 +2331,7 @@ }, "yargs": { "version": "3.32.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", "requires": { "camelcase": "^2.0.1", diff --git a/package.json b/package.json index 3ad6c4a4..60699e43 100644 --- a/package.json +++ b/package.json @@ -4,21 +4,22 @@ "description": "Expand snippets matching the current prefix with `tab`.", "repository": "https://github.com/atom/snippets", "license": "MIT", - "main": "./lib/snippets", + "main": "./snippets", "scripts": { + "generate": "pegjs -c ./lib/parser/config.js", "lint": "standard", "lint:fix": "npm run lint -- --fix", - "test": "npm run lint:fix && atom --test spec", - "generate": "pegjs -c ./lib/parser/config.js" + "test": "npm run lint:fix && atom --test spec" }, "dependencies": { + "atom-select-list": "^0.7.2", "scoped-property-store": "^0.17.0", "season": "^6.0.2" }, "devDependencies": { + "mock-fs": "^4.13.0", "pegjs": "^0.11.0-master.b7b87ea", - "standard": "^14.3.4", - "temp": "^0.9.1" + "standard": "^14.3.4" }, "engines": { "atom": ">=1.47.0", diff --git a/lib/parser/config.js b/parser/config.js similarity index 76% rename from lib/parser/config.js rename to parser/config.js index 4f47b114..26f8c802 100644 --- a/lib/parser/config.js +++ b/parser/config.js @@ -1,6 +1,6 @@ module.exports = { - input: './lib/parser/snippet-body-parser.pegjs', - output: './lib/parser/snippet-body-parser.js', + input: './parser/snippet-body-parser.pegjs', + output: './parser/snippet-body-parser.js', dependencies: { Snippet: '../constructs/snippet', Tabstop: '../constructs/tabstop', diff --git a/lib/parser/snippet-body-parser.js b/parser/snippet-body-parser.js similarity index 100% rename from lib/parser/snippet-body-parser.js rename to parser/snippet-body-parser.js diff --git a/lib/parser/snippet-body-parser.pegjs b/parser/snippet-body-parser.pegjs similarity index 100% rename from lib/parser/snippet-body-parser.pegjs rename to parser/snippet-body-parser.pegjs diff --git a/lib/snippets-available.js b/snippets-available.js similarity index 100% rename from lib/snippets-available.js rename to snippets-available.js diff --git a/lib/snippets.js b/snippets.js similarity index 62% rename from lib/snippets.js rename to snippets.js index 9b5d65a6..ba255089 100644 --- a/lib/snippets.js +++ b/snippets.js @@ -1,39 +1,33 @@ -const { Emitter, CompositeDisposable, File } = require('atom') +const { CompositeDisposable, File } = require('atom') -const CSON = require('season') +const season = require('season') const path = require('path') const fs = require('fs') const ScopedPropertyStore = require('scoped-property-store') +const parser = require('./parser/snippet-body-parser.js') + module.exports = class Snippets { static async activate () { - this.subscriptions = new CompositeDisposable() + this.disposables = new CompositeDisposable() this.snippetsByScopes = new ScopedPropertyStore() this.packageDisposables = new WeakMap() - this.loaded = false - this.emitter = new Emitter() - - this.subscriptions.add( + this.disposables.add( + { dispose: () => delete this.loaded }, atom.workspace.addOpener(uri => uri === 'atom://.atom/snippets' ? atom.workspace.openTextFile(this.userSnippetsPath) : undefined), - atom.commands.add('atom-text-editor', 'snippets:available', () => { - const editor = atom.workspace.getActiveTextEditor() - this.availableSnippetsView.toggle(editor) - }), + atom.commands.add('atom-text-editor', 'snippets:available', () => + this.availableSnippetsView.toggle(atom.workspace.getActiveTextEditor())), atom.packages.onDidActivatePackage(bundle => this.loadPackage(bundle)), atom.packages.onDidDeactivatePackage(bundle => this.unloadPackage(bundle))) - await Promise.all([ + await (this.loaded = Promise.all([ this.loadUserSnippets(), - ...atom.packages.getActivePackages() - .map(bundle => this.loadPackage(bundle)) - ]) - - this.loaded = true - this.emitter.emit('did-load-snippets') + ...atom.packages.getActivePackages().map(bundle => this.loadPackage(bundle)) + ]).then(() => true)) } static get availableSnippetsView () { @@ -43,18 +37,7 @@ module.exports = class Snippets { return (this.availableSnippetsView = new SnippetsAvailable(this)) } - static deactivate () { - this.emitter.dispose() - this.subscriptions.dispose() - } - - static get parser () { - delete this.parser - - return (this.parser = require('./parser/snippet-body-parser.js')) - } - - static getUserSnippetsPath () { + static get userSnippetsPath () { let userSnippetsPath = path.join(atom.getConfigDirPath(), 'snippets.json') try { fs.accessSync(this.userSnippetsPath) @@ -64,39 +47,27 @@ module.exports = class Snippets { return userSnippetsPath } - static get userSnippetsPath () { - delete this.userSnippetsPath - - return (this.userSnippetsPath = this.getUserSnippetsPath()) - } - static loadSnippetsFile (filepath) { const priority = filepath === this.userSnippetsPath ? 1000 : 0 return new Promise((resolve, reject) => - CSON.readFile(filepath, (error, object) => error == null + season.readFile(filepath, (error, object) => error == null ? resolve(this.snippetsByScopes.addProperties(filepath, object, { priority })) : reject(error))) } static async loadUserSnippets () { + const userSnippetsPath = this.userSnippetsPath try { - const userSnippetsFile = new File(this.userSnippetsPath) - if (this.packageDisposables.has(this)) { - this.packageDisposables.get(this).dispose() - } + const userSnippetsFile = new File(userSnippetsPath) + // Allow user defined snippets to be reloaded + this.unloadPackage(this) this.packageDisposables.set(this, new CompositeDisposable( - await this.loadSnippetsFile(this.userSnippetsPath), + await this.loadSnippetsFile(userSnippetsPath), userSnippetsFile.onDidChange(() => this.loadUserSnippets()), - userSnippetsFile.onDidDelete(() => { - this.packageDisposables.get(this).dispose() - this.userSnippetsPath = this.getUserSnippetsPath() - }), - userSnippetsFile.onDidRename(() => { - this.packageDisposables.get(this).dispose() - this.userSnippetsPath = this.getUserSnippetsPath() - }))) + userSnippetsFile.onDidDelete(() => this.loadUserSnippets()), + userSnippetsFile.onDidRename(() => this.loadUserSnippets()))) } catch (error) { - atom.notifications.addWarning(`Unable to load snippets from: '${this.userSnippetsPath}'`, { + atom.notifications.addWarning(`Unable to load snippets from: '${userSnippetsPath}'`, { description: 'Make sure you have permissions to access the directory and file.', detail: error.toString(), dismissable: true @@ -124,11 +95,15 @@ module.exports = class Snippets { } }) } catch (error) { - if (error.code === 'ENOTDIR' || error.code === 'ENOENT') { - // Path either doesn't exist, or isn't a directory - return + if (error.code !== 'ENOTDIR' && error.code !== 'ENOENT') { + atom.notifications.addError(`Error reading snippets directory ${snippetsDirectory}`, { + description: 'Make sure you have permissions to access the directory and file.', + detail: error.toString(), + stack: error.stack, + dismissable: true + }) } - console.warn(`Error reading snippets directory ${snippetsDirectory}`, error) + // Path either doesn't exist, or isn't a directory } } @@ -139,16 +114,16 @@ module.exports = class Snippets { } } - static onDidLoadSnippets (callback) { - this.emitter.on('did-load-snippets', callback) - } - static snippets () { return { - parse: string => this.parser.parse(string), + parse: string => parser.parse(string), userSnippetsPath: () => this.userSnippetsPath, snippetsByScopes: () => this.snippetsByScopes, - loaded: () => this.loaded + loaded: () => this.loaded || Promise.resolve(false) } } + + static get deactivate () { + return this.disposables.dispose + } } diff --git a/spec/snippet-spec.js b/spec/snippet-spec.js deleted file mode 100644 index e69de29b..00000000 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('') + }) +}) From f0a1ff6d5cca1ac2542e90a8f972157e0d0d63b9 Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Thu, 22 Oct 2020 03:03:42 +0300 Subject: [PATCH 15/17] temp take 3? im second guessing myself --- constructs/snippet.js | 20 +++++++++----------- constructs/tabstop.js | 2 +- constructs/variable.js | 7 ++++--- snippets.js | 24 ++++++++++++------------ 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/constructs/snippet.js b/constructs/snippet.js index d99f6db9..41e5dac2 100644 --- a/constructs/snippet.js +++ b/constructs/snippet.js @@ -170,8 +170,9 @@ module.exports = class Snippet extends Construct { // helper cause Snippet isn't really ever available expand ({ - cursor = atom.workspace.getActiveTextEditor().getLastCursor(), - tabstops = cursor.editor.addMarkerLayer(), + editor = atom.workspace.getActiveTextEditor(), + cursor = editor.getLastCursor(), + tabstops = editor.addMarkerLayer(), variables = {} } = {}) { if (this.legacySyntax) { @@ -207,8 +208,8 @@ module.exports = class Snippet extends Construct { this.body.forEach(value => { value instanceof Object - ? value.expand(cursor, tabstops, variables) - : this.insert(cursor, value) + ? value.expand(editor, cursor, tabstops, variables) + : this.insert(editor, cursor.getBufferPosition(), value) }) // Only create tabstop stuff if we have any @@ -218,13 +219,10 @@ module.exports = class Snippet extends Construct { // 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 = { - tabstops: Snippet.getTabstops(tabstops.getMarkers()), - get iterator () { - delete this.iterator - return (this.iterator = this.tabstops.values()) - }, - next () { + iterator: Snippet.getTabstops(tabstops.getMarkers()).values(), + next (event) { const { done, value: [stop, ...mirrors] = [] } = this.iterator.next() + const editor = event.originalEvent.getModel(); return done ? disposables.dispose() // Cheaty way of returning true concisely @@ -236,7 +234,7 @@ module.exports = class Snippet extends Construct { disposables.add( { dispose: () => tabstops.destroy() }, atom.keymaps.add(module.filename, { [target]: { tab: iterate } }), - atom.commands.add(target, iterate, event => stops.next() || + atom.commands.add(target, iterate, event => stops.next(event) || event.abortKeyBinding())) // Go to the first tabstop diff --git a/constructs/tabstop.js b/constructs/tabstop.js index 9c37b7d7..a85ab6e1 100644 --- a/constructs/tabstop.js +++ b/constructs/tabstop.js @@ -1,7 +1,7 @@ const Construct = require('./construct') module.exports = class Tabstop extends Construct { - expand (cursor, tabstops, variables) { + expand (editor, cursor, tabstops, variables) { this.mark({ tabstops, start: cursor.getBufferPosition() }) } } diff --git a/constructs/variable.js b/constructs/variable.js index 2daf1eaa..31cf8307 100644 --- a/constructs/variable.js +++ b/constructs/variable.js @@ -1,9 +1,10 @@ const Construct = require('./construct') module.exports = class Variable extends Construct { - expand (cursor, tabstops, variables) { + expand (editor, cursor, tabstops, variables) { + const position = cursor.getBufferPosition(); this.identifier in variables - ? this.insert(cursor, variables[this.identifier]) - : this.mark({ tabstops, ...this.insert(cursor, this.identifier) }) + ? this.insert(editor, position, variables[this.identifier]) + : this.mark({ tabstops, ...this.insert(editor, position, this.identifier) }) } } diff --git a/snippets.js b/snippets.js index ba255089..26bdb957 100644 --- a/snippets.js +++ b/snippets.js @@ -21,12 +21,12 @@ module.exports = class Snippets { : undefined), atom.commands.add('atom-text-editor', 'snippets:available', () => this.availableSnippetsView.toggle(atom.workspace.getActiveTextEditor())), - atom.packages.onDidActivatePackage(bundle => this.loadPackage(bundle)), - atom.packages.onDidDeactivatePackage(bundle => this.unloadPackage(bundle))) + atom.packages.onDidActivatePackage(pack => this.loadPackage(pack)), + atom.packages.onDidDeactivatePackage(pack => this.unloadPackage(pack))) await (this.loaded = Promise.all([ this.loadUserSnippets(), - ...atom.packages.getActivePackages().map(bundle => this.loadPackage(bundle)) + ...atom.packages.getActivePackages().map(pack => this.loadPackage(pack)) ]).then(() => true)) } @@ -75,17 +75,17 @@ module.exports = class Snippets { } } - static async loadPackage (bundle) { - const snippetsDirectory = path.join(bundle.path, 'snippets') + static async loadPackage (pack) { + const snippetsDirectory = path.join(pack.path, 'snippets') try { const files = await fs.promises.readdir(snippetsDirectory) files.forEach(async file => { const snippetsFile = path.join(snippetsDirectory, file) try { const disposable = await this.loadSnippetsFile(snippetsFile) - this.packageDisposables.has(bundle) - ? this.packageDisposables.get(bundle).add(disposable) - : this.packageDisposables.set(bundle, new CompositeDisposable(disposable)) + this.packageDisposables.has(pack) + ? this.packageDisposables.get(pack).add(disposable) + : this.packageDisposables.set(pack, new CompositeDisposable(disposable)) } catch (error) { atom.notifications.addWarning(`Unable to load snippets from: '${snippetsFile}'`, { description: 'Make sure you have permissions to access the directory and file.', @@ -107,10 +107,10 @@ module.exports = class Snippets { } } - static unloadPackage (bundle) { - if (this.packageDisposables.has(bundle)) { - this.packageDisposables.get(bundle).dispose() - this.packageDisposables.delete(bundle) + static unloadPackage (pack) { + if (this.packageDisposables.has(pack)) { + this.packageDisposables.get(pack).dispose() + this.packageDisposables.delete(pack) } } From e7d8455045ac346f1ee9934d6d213fcfcd2b55e9 Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Tue, 8 Dec 2020 16:09:50 +0200 Subject: [PATCH 16/17] implement missing functionality from master i made some false assumptions about how stuff worked But now this should be onpar with master, kinda The babel stuff will be removed once i clean this up --- available-snippets-view.js | 44 + babel.config.json | 6 + constructs/babel.config.json | 6 + constructs/construct.js | 16 +- constructs/snippet.js | 254 ++---- constructs/variable.js | 5 +- menus/snippets.cson | 12 - modifiers/babel.config.json | 6 + modifiers/choice.js | 38 +- modifiers/modifier.js | 6 +- modifiers/placeholder.js | 18 +- modifiers/transformation.js | 54 +- package-lock.json | 1431 ++++++++++++++++++------------ package.json | 21 +- parser/snippet-body-parser.js | 142 ++- parser/snippet-body-parser.pegjs | 10 +- snippets-available.js | 83 -- snippets.js | 193 ++-- 18 files changed, 1261 insertions(+), 1084 deletions(-) create mode 100644 available-snippets-view.js create mode 100644 babel.config.json create mode 100644 constructs/babel.config.json delete mode 100644 menus/snippets.cson create mode 100644 modifiers/babel.config.json delete mode 100644 snippets-available.js diff --git a/available-snippets-view.js b/available-snippets-view.js new file mode 100644 index 00000000..d73b4771 --- /dev/null +++ b/available-snippets-view.js @@ -0,0 +1,44 @@ +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 }]) => { + const li = document.createElement('li') + li.classList.add('two-lines') + + const primaryLine = document.createElement('div') + primaryLine.classList.add('primary-line') + primaryLine.textContent = prefix + li.appendChild(primaryLine) + + const secondaryLine = document.createElement('div') + secondaryLine.classList.add('secondary-line') + secondaryLine.textContent = name + li.appendChild(secondaryLine) + + 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/constructs/babel.config.json b/constructs/babel.config.json new file mode 100644 index 00000000..f0525c14 --- /dev/null +++ b/constructs/babel.config.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + "@babel/plugin-proposal-class-properties", + "@babel/plugin-proposal-private-methods" + ] +} diff --git a/constructs/construct.js b/constructs/construct.js index 473d11ab..60156b8f 100644 --- a/constructs/construct.js +++ b/constructs/construct.js @@ -3,27 +3,29 @@ module.exports = class Construct { this.identifier = identifier } - insert (cursor, value) { - return cursor.editor.getBuffer().insert(cursor.getBufferPosition(), value) + expand () {} + + insert (editor, cursor, value) { + return editor.getBuffer().insert(cursor.getBufferPosition(), value) } - activate (mirror, cursor, stop) { + activate (editor, cursor, stop, mirror) { if (mirror === stop) { - cursor.selection.setBufferRange(stop.getRange()) + cursor.selection.setBufferRange(stop.getBufferRange()) const subscription = cursor.onDidChangePosition(({ newBufferPosition }) => { - if (!stop.getRange().containsPoint(newBufferPosition)) { + if (!stop.getBufferRange().containsPoint(newBufferPosition)) { stop.destroy() subscription.dispose() } }) } else { - cursor.editor.decorateMarker(mirror, { type: 'highlight' }) + editor.decorateMarker(mirror, { type: 'highlight' }) stop.onDidDestroy(() => mirror.destroy()) } } mark ({ tabstops, start, end = start, exclusive = true, construct = this }) { - tabstops.markRange({ start, end }, { exclusive }).setProperties({ construct }) + tabstops.markBufferRange({ start, end }, { exclusive }).setProperties({ construct }) } toString () { diff --git a/constructs/snippet.js b/constructs/snippet.js index 41e5dac2..474d302a 100644 --- a/constructs/snippet.js +++ b/constructs/snippet.js @@ -4,136 +4,72 @@ const path = require('path') const Construct = require('./construct') module.exports = class Snippet extends Construct { - static getVariables (cursor) { - // Lazy getters for each variable - return { - // The currently selected text or the empty string - get TM_SELECTED_TEXT () { - delete this.TM_SELECTED_TEXT - return (this.TM_SELECTED_TEXT = cursor.selection.getText()) - }, - // The contents of the current line - get TM_CURRENT_LINE () { - delete this.TM_CURRENT_LINE - return (this.TM_CURRENT_LINE = cursor.getCurrentBufferLine()) - }, - // The contents of the word under cursor or the empty string - get TM_CURRENT_WORD () { - delete this.TM_CURRENT_WORD - return (this.TM_CURRENT_WORD = cursor.editor.getTextInBufferRange(cursor.getCurrentWordBufferRange())) - }, - // The zero-index based line number - get TM_LINE_INDEX () { - delete this.TM_LINE_INDEX - return (this.TM_LINE_INDEX = cursor.getBufferRow().toString()) - }, - // The one-index based line number - get TM_LINE_NUMBER () { - delete this.TM_LINE_NUMBER - // Does 'getScreenRow'work as intended? - return (this.TM_LINE_NUMBER = (cursor.getScreenRow() + 1).toString()) - }, - // The filename of the current document - get TM_FILENAME () { - delete this.TM_FILENAME - return (this.TM_FILENAME = cursor.editor.getTitle()) - }, - // The filename of the current document without its extensions - get TM_FILENAME_BASE () { - delete this.TM_FILENAME_BASE - const filepath = cursor.editor.getTitle() - return (this.TM_FILENAME_BASE = path.basename(filepath, path.extname(filepath))) - }, - // The directory of the current document - get TM_DIRECTORY () { - delete this.TM_DIRECTORY - return (this.TM_DIRECTORY = path.dirname(cursor.editor.getPath())) - }, - // The full file path of the current document - get TM_FILEPATH () { - delete this.TM_FILEPATH - return (this.TM_FILEPATH = cursor.editor.getPath()) - }, - // The contents of the clipboard - get CLIPBOARD () { - delete this.CLIPBOARD - return (this.CLIPBOARD = atom.clipboard.read()) - }, - // The name of the opened workspace or folder - get WORKSPACE_NAME () { - delete this.WORKSPACE_NAME - const [projectPath] = atom.project.relativizePath(cursor.editor.getPath()) - return (this.WORKSPACE_NAME = path.basename(projectPath)) - }, - // Insert the current date and time - // The current year - get CURRENT_YEAR () { - delete this.CURRENT_YEAR - return (this.CURRENT_YEAR = new Date().toLocaleString('default', { year: 'numeric' })) - }, - // The current year's last two digits - get CURRENT_YEAR_SHORT () { - delete this.CURRENT_YEAR_SHORT - return (this.CURRENT_YEAR_SHORT = new Date().toLocaleString('default', { year: '2-digit' })) - }, - // The month as two digits - get CURRENT_MONTH () { - delete this.CURRENT_MONTH - return (this.CURRENT_MONTH = new Date().toLocaleString('default', { month: '2-digit' })) - }, - // The full name of the month - get CURRENT_MONTH_NAME () { - delete this.CURRENT_MONTH_NAME - return (this.CURRENT_MONTH_NAME = new Date().toLocaleString('default', { month: 'long' })) - }, - // The short name of the month - get CURRENT_MONTH_NAME_SHORT () { - delete this.CURRENT_MONTH_NAME_SHORT - return (this.CURRENT_MONTH_NAME_SHORT = new Date().toLocaleString('default', { month: 'short' })) - }, - // The day of the month - get CURRENT_DATE () { - delete this.CURRENT_DATE - return (this.CURRENT_DATE = new Date().toLocaleString('default', { day: '2-digit' })) - }, - // The name of day - get CURRENT_DAY_NAME () { - delete this.CURRENT_DAY_NAME - return (this.CURRENT_DAY_NAME = new Date().toLocaleString('default', { weekday: 'long' })) - }, - // The short name of the day - get CURRENT_DAY_NAME_SHORT () { - delete this.CURRENT_DAY_NAME_SHORT - return (this.CURRENT_DAY_NAME_SHORT = new Date().toLocaleString('default', { weekday: 'short' })) - }, - // The current hour in 24-hour clock format - get CURRENT_HOUR () { - delete this.CURRENT_HOUR - return (this.CURRENT_HOUR = new Date().toLocaleString('default', { hour: '2-digit' })) - }, - // The current minute - get CURRENT_MINUTE () { - delete this.CURRENT_MINUTE - return (this.CURRENT_MINUTE = new Date().toLocaleString('default', { minute: '2-digit' })) - }, - // The current second - get CURRENT_SECOND () { - delete this.CURRENT_SECOND - return (this.CURRENT_SECOND = new Date().toLocaleString('default', { second: '2-digit' })) - }, - // The number of seconds since the Unix epoch - get CURRENT_SECONDS_UNIX () { - delete this.CURRENT_SECONDS_UNIX - return (this.CURRENT_SECONDS_UNIX = Math.round(new Date() / 1000).toString()) - } - /* - TODO?: - Insert line or block comments, honoring the current language - BLOCK_COMMENT_START - BLOCK_COMMENT_END - LINE_COMMENT - */ - } + 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 + PROXY: (editor, cursor, custom) => new Proxy({}, { + get: (cache, property) => property in this.VARIABLES + ? (cache[property] = this.VARIABLES[property]()) + : 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) { @@ -145,8 +81,8 @@ module.exports = class Snippet extends Construct { Number.isInteger(construct.identifier) ? Array.isArray(tabstops[construct.identifier]) - ? tabstops[construct.identifier].push(marker) - : tabstops[construct.identifier] = [marker] + ? tabstops[construct.identifier].push(marker) + : tabstops[construct.identifier] = [marker] : unknowns.push([marker]) }) // Include all unknown variables at the end @@ -161,18 +97,23 @@ module.exports = class Snippet extends Construct { constructor (body, legacySyntax) { // This snippet will work as the default ending tabstop - super(0, false) + super(0) this.body = body this.legacySyntax = legacySyntax } + // We work as the default ending tabstop, this is a special case + activate (editor, cursor, stop, mirror) { + cursor.setBufferPosition(stop.getBufferRange().end) + } + // helper cause Snippet isn't really ever available expand ({ editor = atom.workspace.getActiveTextEditor(), cursor = editor.getLastCursor(), - tabstops = editor.addMarkerLayer(), + tabstops = editor.addMarkerLayer(), // This is a _Display_MarkerLayer variables = {} } = {}) { if (this.legacySyntax) { @@ -182,11 +123,8 @@ module.exports = class Snippet extends Construct { }) } - // Construct variables from given and global - Object.defineProperties(variables, - Object.getOwnPropertyDescriptors(Snippet.getVariables(cursor))) - - const disposables = new CompositeDisposable() + // Construct 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 @@ -194,11 +132,11 @@ module.exports = class Snippet extends Construct { this.mark({ tabstops, start: cursor.getBufferPosition(), exclusive: false }) const marker = tabstops.getMarkers().pop() // Take care that our disposables are disposed if necessary - disposables.add( + const disposables = new CompositeDisposable( cursor.onDidDestroy(() => disposables.dispose()), cursor.onDidChangePosition(({ newBufferPosition }) => { // Exclude endpoints, so that end tabstops don't trigger mirror logic - if (!marker.getRange().containsPoint(newBufferPosition, true)) { + if (!marker.getBufferRange().containsPoint(newBufferPosition, true)) { disposables.dispose() } })) @@ -206,11 +144,9 @@ module.exports = class Snippet extends Construct { // 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.getBufferPosition(), value) - }) + 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) { @@ -220,33 +156,29 @@ module.exports = class Snippet extends Construct { // Luckilly the ids used are integers and the values are fetched using 'Object.values' const stops = { iterator: Snippet.getTabstops(tabstops.getMarkers()).values(), - next (event) { - const { done, value: [stop, ...mirrors] = [] } = this.iterator.next() - const editor = event.originalEvent.getModel(); - return done - ? disposables.dispose() - // Cheaty way of returning true concisely - : [stop, ...mirrors].every(mirror => !stop.getProperties() - .construct.activate(mirror, cursor, stop)) + next () { + const iteration = this.iterator.next() + if (!iteration.done) { + const { value: [stop] } = iteration + const { construct } = stop.getProperties() + iteration.value.forEach(mirror => construct.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) || + atom.commands.add(target, iterate, event => stops.next() || event.abortKeyBinding())) // Go to the first tabstop stops.next() } - return marker.getRange() - } - - // We work as the default ending tabstop, this is a special case - activate (marker, cursor) { - cursor.setBufferPosition(marker.getRange().end) + return marker.getBufferRange() } toString () { diff --git a/constructs/variable.js b/constructs/variable.js index 31cf8307..de45b393 100644 --- a/constructs/variable.js +++ b/constructs/variable.js @@ -2,9 +2,8 @@ const Construct = require('./construct') module.exports = class Variable extends Construct { expand (editor, cursor, tabstops, variables) { - const position = cursor.getBufferPosition(); this.identifier in variables - ? this.insert(editor, position, variables[this.identifier]) - : this.mark({ tabstops, ...this.insert(editor, position, this.identifier) }) + ? this.insert(editor, cursor, variables[this.identifier]) + : this.mark({ tabstops, ...this.insert(editor, cursor, this.identifier) }) } } 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/modifiers/babel.config.json b/modifiers/babel.config.json new file mode 100644 index 00000000..f0525c14 --- /dev/null +++ b/modifiers/babel.config.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + "@babel/plugin-proposal-class-properties", + "@babel/plugin-proposal-private-methods" + ] +} diff --git a/modifiers/choice.js b/modifiers/choice.js index 7c350ad0..40220129 100644 --- a/modifiers/choice.js +++ b/modifiers/choice.js @@ -7,24 +7,21 @@ module.exports = class Choice extends Modifier { super() 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] } - create ([Construct, ...args]) { - class Choice extends Construct { - constructor ({ choices: [first, ...rest] }, ...args) { - super(...args) - - this.default = first - // Move the default last, so choice cycling works as expected - this.choices = [...rest, first] - } - - activate (marker, cursor, mirror, target, command) { + modify (construct) { + class Choice extends construct.constructor { + 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) { + if (mirror === stop) { const disposables = new CompositeDisposable() - const cycle = `snippets:next-choice-${marker.id}` + const target = 'atom-text-editor:not([mini])' + const cycle = `snippets:next-choice-${stop.id}` const choices = { choices: this.choices, @@ -34,27 +31,28 @@ module.exports = class Choice extends Modifier { const { value } = iteration.done ? (this.iterator = this.choices.values()).next() : iteration - cursor.selection.insertText(value, { select: true }) + 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 (!marker.getRange().containsPoint(newBufferPosition)) { + if (!stop.getBufferRange().containsPoint(newBufferPosition)) { disposables.dispose() } })) } - return super.activate(marker, cursor, mirror, target, command) } - expand (cursor, tabstops, variables) { + expand (editor, cursor, tabstops, variables) { if (!(this.identifier in variables)) { - this.mark({ tabstops, ...this.insert(cursor, this.default) }) + this.mark({ tabstops, ...this.insert(editor, cursor, this.default) }) } else { - super.expand(cursor, tabstops, variables) + super.expand(editor, cursor, tabstops, variables) } } @@ -63,6 +61,6 @@ module.exports = class Choice extends Modifier { } } - return new Choice(this, ...args) + return Object.assign(new Choice(construct), this) } } diff --git a/modifiers/modifier.js b/modifiers/modifier.js index f41ae4b1..879235dc 100644 --- a/modifiers/modifier.js +++ b/modifiers/modifier.js @@ -1,6 +1,6 @@ module.exports = class Modifier { - create ([Construct, ...args]) { - // Don't actually modify the Construct - return new Construct(...args) + modify (construct) { + // Don't actually modify the construct + return construct } } diff --git a/modifiers/placeholder.js b/modifiers/placeholder.js index 46a14f3e..a9fbae04 100644 --- a/modifiers/placeholder.js +++ b/modifiers/placeholder.js @@ -9,19 +9,13 @@ module.exports = class Placeholder extends Modifier { this.snippet.legacySyntax = false } - create ([Construct, ...args]) { - class Placeholder extends Construct { - constructor ({ snippet }, ...args) { - super(...args) - - this.snippet = snippet - } - - expand (cursor, tabstops, variables) { + modify (construct) { + class Placeholder extends construct.constructor { + expand (editor, cursor, tabstops, variables) { if (!(this.identifier in variables)) { - this.mark({ tabstops, ...this.snippet.expand({ cursor, tabstops, variables }) }) + this.mark({ tabstops, ...this.snippet.expand({ editor, cursor, tabstops, variables }) }) } else { - super.expand(cursor, tabstops, variables) + super.expand(editor, cursor, tabstops, variables) } } @@ -30,6 +24,6 @@ module.exports = class Placeholder extends Modifier { } } - return new Placeholder(this, ...args) + return Object.assign(new Placeholder(construct), this) } } diff --git a/modifiers/transformation.js b/modifiers/transformation.js index b8becc8d..0f7500da 100644 --- a/modifiers/transformation.js +++ b/modifiers/transformation.js @@ -8,45 +8,39 @@ module.exports = class Transformation extends Modifier { this.format = format } - create ([Construct, ...args]) { - class Transformation extends Construct { - constructor ({ regexp, format }, ...args) { - super(...args) - - this.regexp = regexp - this.format = format - - this.transform = (string, regexp, fold = sequence => sequence) => - 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 - }, '') - } - - activate (mirror, cursor, stops) { + modify (construct) { + class Transformation extends construct.constructor { + activate (editor, cursor, stop, mirror) { + super.activate(editor, cursor, stop, mirror) mirror.onDidDestroy(() => { - const range = mirror.getRange() - const buffer = cursor.editor.getBuffer() - const text = this.transform(buffer.getTextInRange(range), this.regexp) - buffer.setTextInRange(range, text) + const range = mirror.getBufferRange() + const buffer = editor.getBuffer() + buffer.setTextInRange(range, this.transform(buffer.getTextInRange(range))) }) - super.activate(mirror, cursor, stops) } - insert (cursor, value) { - return super.insert(cursor, this.transform(value, this.regexp)) + 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(), this.regexp) + return this.transform(super.toString()) } } - return new Transformation(this, ...args) + return Object.assign(new Transformation(construct), this) } } diff --git a/package-lock.json b/package-lock.json index a4a07d91..0ab8423e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "snippets-dev", + "name": "snippets", "version": "2.0.0", "lockfileVersion": 1, "requires": true, @@ -13,12 +13,223 @@ "@babel/highlight": "^7.10.4" } }, + "@babel/core": { + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", + "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.7", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.9", + "@babel/types": "^7.12.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/eslint-parser": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.12.1.tgz", + "integrity": "sha512-cc7WQHnHQY3++/bghgbDtPx+5bf6xTsokyGzV6Qzh65NLz/unv+mPQuACkQ9GFhIhcTFv6yqwNaEcfX7EkOEsg==", + "dev": true, + "requires": { + "eslint-scope": "5.1.0", + "eslint-visitor-keys": "^1.3.0", + "semver": "^6.3.0" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "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 + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", + "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.10.4" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", + "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "dev": true, + "requires": { + "@babel/types": "^7.12.7" + } + }, + "@babel/helper-module-imports": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.7.tgz", + "integrity": "sha512-I5xc9oSJ2h59OwyUqjv95HRyzxj53DAubUERgQMrpcCEYQyToeHA+NEcUEsVWB4j53RDeskeBJ0SgRAYHDBckw==", + "dev": true, + "requires": { + "@babel/types": "^7.12.7" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, "@babel/helper-validator-identifier": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", "dev": true }, + "@babel/helpers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, "@babel/highlight": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", @@ -28,18 +239,122 @@ "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + }, + "dependencies": { + "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" + } + } } }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "@babel/parser": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz", + "integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==", + "dev": true + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", + "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", + "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/template": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", + "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7" + } + }, + "@babel/traverse": { + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz", + "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", + "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@eslint/eslintrc": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz", + "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==", + "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" + } + }, + "@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.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", - "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true }, "acorn-jsx": { @@ -49,9 +364,9 @@ "dev": true }, "ajv": { - "version": "6.12.5", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", - "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", + "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", @@ -60,22 +375,11 @@ "uri-js": "^4.2.2" } }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, - "requires": { - "type-fest": "^0.11.0" - }, - "dependencies": { - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true - } - } + "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", @@ -111,6 +415,73 @@ "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" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.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" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -150,6 +521,16 @@ "concat-map": "0.0.1" } }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -162,37 +543,56 @@ "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==", + "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": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "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" + } + }, + "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 + }, + "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 + }, + "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" + } + } } }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", @@ -208,11 +608,6 @@ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, - "coffee-script": { - "version": "1.12.7", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", - "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==" - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -239,33 +634,24 @@ "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", "dev": true }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "safe-buffer": "~5.1.1" } }, - "cson-parser": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-1.3.5.tgz", - "integrity": "sha1-fsZ14DkUVTO/KmqFYHPxWZ2cLSQ=", + "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": { - "coffee-script": "^1.10.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, "d": { @@ -277,20 +663,14 @@ } }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, - "debug-log": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", - "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", - "dev": true - }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -311,28 +691,6 @@ "object-keys": "^1.0.12" } }, - "deglob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/deglob/-/deglob-4.0.1.tgz", - "integrity": "sha512-/g+RDZ7yf2HvoW+E5Cy+K94YhgcFgr6C8LuHZD1O5HoNPkf3KY6RfXJ0DBGlB/NkLi5gml+G9zqRzk9S0mHZCg==", - "dev": true, - "requires": { - "find-root": "^1.0.0", - "glob": "^7.0.5", - "ignore": "^5.0.0", - "pkg-config": "^1.1.0", - "run-parallel": "^1.1.2", - "uniq": "^1.0.1" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - } - } - }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -354,11 +712,20 @@ } }, "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "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", @@ -369,20 +736,20 @@ } }, "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" } @@ -475,22 +842,24 @@ "dev": true }, "eslint": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", - "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.13.0.tgz", + "integrity": "sha512-uCORMuOO8tUzJmsdRtrvcGq5qposf7Rw0LwkTJkoDbOycVQtQjmnhZSuLQnozLE4TmAzlMVV45eCHmQ1OpDKUQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.2.1", "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.3", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.2", - "esquery": "^1.0.1", + "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", @@ -499,53 +868,51 @@ "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "inquirer": "^7.0.0", "is-glob": "^4.0.0", "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", + "levn": "^0.4.1", + "lodash": "^4.17.19", "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.3", + "optionator": "^0.9.1", "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", + "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": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "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==", + "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": "^4.1.0" + "ansi-regex": "^5.0.0" } } } }, "eslint-config-standard": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz", - "integrity": "sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg==", + "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 }, "eslint-config-standard-jsx": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-8.1.0.tgz", - "integrity": "sha512-ULVC8qH8qCqbU792ZOO6DaiaZyHNS/5CZt3hKqHkEhVlhPEPN3nfBqqxJCyp59XrjIBZPu1chMYe9T2DXZ7TMw==", + "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 }, "eslint-import-resolver-node": { @@ -603,40 +970,34 @@ } }, "eslint-plugin-es": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-2.0.0.tgz", - "integrity": "sha512-f6fceVtg27BR02EYnBhgWLFQfK6bN4Ll0nQFrBHOlCsAyxeZkn0NHns5O0YZOPrV1B3ramd6cgFwaoFLcSkwEQ==", + "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": { - "eslint-utils": "^1.4.2", + "eslint-utils": "^2.0.0", "regexpp": "^3.0.0" - }, - "dependencies": { - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true - } } }, "eslint-plugin-import": { - "version": "2.18.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", - "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==", + "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": { - "array-includes": "^3.0.3", + "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.2", - "eslint-module-utils": "^2.4.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.0", + "object.values": "^1.1.1", "read-pkg-up": "^2.0.0", - "resolve": "^1.11.0" + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" }, "dependencies": { "debug": { @@ -667,13 +1028,13 @@ } }, "eslint-plugin-node": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-10.0.0.tgz", - "integrity": "sha512-1CSyM/QCjs6PXaT18+zuAXsjXGIGo5Rw630rSKwokSs2jrYURQc4R5JZpoanNCqwNmepg+0eZ9L7YiRUJb8jiQ==", + "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": "^2.0.0", - "eslint-utils": "^1.4.2", + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", "ignore": "^5.1.1", "minimatch": "^3.0.4", "resolve": "^1.10.1", @@ -685,6 +1046,12 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, @@ -695,20 +1062,22 @@ "dev": true }, "eslint-plugin-react": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz", - "integrity": "sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA==", + "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.0.3", + "array-includes": "^3.1.1", + "array.prototype.flatmap": "^1.2.3", "doctrine": "^2.1.0", "has": "^1.0.3", - "jsx-ast-utils": "^2.1.0", - "object.entries": "^1.1.0", - "object.fromentries": "^2.0.0", - "object.values": "^1.1.0", + "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.10.1" + "resolve": "^1.18.1", + "string.prototype.matchall": "^4.0.2" }, "dependencies": { "doctrine": { @@ -722,12 +1091,6 @@ } } }, - "eslint-plugin-standard": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", - "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", - "dev": true - }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -739,29 +1102,45 @@ } }, "eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "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" + }, + "dependencies": { + "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 + } } }, "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==", + "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 }, "espree": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", - "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", + "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", "dev": true, "requires": { - "acorn": "^7.1.1", + "acorn": "^7.4.0", "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "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 + } } }, "esprima": { @@ -844,17 +1223,6 @@ } } }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -869,18 +1237,9 @@ }, "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 - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } + "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", @@ -891,12 +1250,6 @@ "flat-cache": "^2.0.1" } }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -967,10 +1320,27 @@ "resolved": "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz", "integrity": "sha1-kCBMPi/appQbso0WZF1BgGOpDps=" }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-stdin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", - "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", "dev": true }, "glob": { @@ -1045,15 +1415,6 @@ "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", "dev": true }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -1061,9 +1422,9 @@ "dev": true }, "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -1090,109 +1451,15 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "internal-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.2.tgz", + "integrity": "sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g==", "dev": true, "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "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": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "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 - }, - "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 - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "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": "^5.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" - } - } + "es-abstract": "^1.17.0-next.1", + "has": "^1.0.3", + "side-channel": "^1.0.2" } }, "invert-kv": { @@ -1207,11 +1474,20 @@ "dev": true }, "is-callable": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz", - "integrity": "sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", "dev": true }, + "is-core-module": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", + "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", + "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", @@ -1299,6 +1575,12 @@ "esprima": "^4.0.0" } }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, "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", @@ -1317,14 +1599,23 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "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" + } + }, "jsx-ast-utils": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", - "integrity": "sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.1.0.tgz", + "integrity": "sha512-d4/UOjg+mxAWxCiF0c5UTSwyqbchkbqCvK87aBovhnh8GtysTjWmgC63tY0cJx/HzGgm9qnA147jVBdpOiQ2RA==", "dev": true, "requires": { "array-includes": "^3.1.1", - "object.assign": "^4.1.0" + "object.assign": "^4.1.1" } }, "key-path-helpers": { @@ -1341,13 +1632,13 @@ } }, "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "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.1.2", - "type-check": "~0.3.2" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" } }, "load-json-file": { @@ -1387,12 +1678,6 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1431,12 +1716,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -1448,12 +1727,6 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -1498,37 +1771,15 @@ "dev": true }, "object.assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", - "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "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", - "es-abstract": "^1.18.0-next.0", "has-symbols": "^1.0.1", "object-keys": "^1.1.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0-next.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz", - "integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } } }, "object.entries": { @@ -1574,43 +1825,28 @@ "wrappy": "1" } }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "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.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" + "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": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "requires": { "lcid": "^1.0.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=", - "dev": true - }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -1665,9 +1901,9 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-parse": { @@ -1787,17 +2023,6 @@ } } }, - "pkg-config": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", - "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", - "dev": true, - "requires": { - "debug-log": "^1.0.0", - "find-root": "^1.0.0", - "xtend": "^4.0.1" - } - }, "pkg-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", @@ -1808,9 +2033,9 @@ } }, "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, "progress": { @@ -1872,18 +2097,29 @@ "read-pkg": "^2.0.0" } }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", "dev": true }, "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, "requires": { + "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } }, @@ -1893,16 +2129,6 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -1911,31 +2137,10 @@ "glob": "^7.1.3" } }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true - }, - "rxjs": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", - "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "scoped-property-store": { @@ -1958,34 +2163,75 @@ "cson-parser": "^1.3.0", "fs-plus": "^3.0.0", "yargs": "^3.23.0" + }, + "dependencies": { + "coffee-script": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", + "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==" + }, + "cson-parser": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-1.3.5.tgz", + "integrity": "sha1-fsZ14DkUVTO/KmqFYHPxWZ2cLSQ=", + "requires": { + "coffee-script": "^1.10.0" + } + } } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true }, "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "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": "^1.0.0" + "shebang-regex": "^3.0.0" } }, "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true + "side-channel": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.3.tgz", + "integrity": "sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==", + "dev": true, + "requires": { + "es-abstract": "^1.18.0-next.0", + "object-inspect": "^1.8.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } }, "slice-ansi": { "version": "2.1.0", @@ -2006,6 +2252,12 @@ } } }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -2033,9 +2285,9 @@ } }, "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", "dev": true }, "sprintf-js": { @@ -2045,32 +2297,31 @@ "dev": true }, "standard": { - "version": "14.3.4", - "resolved": "https://registry.npmjs.org/standard/-/standard-14.3.4.tgz", - "integrity": "sha512-+lpOkFssMkljJ6eaILmqxHQ2n4csuEABmcubLTb9almFi1ElDzXb1819fjf/5ygSyePCq4kU2wMdb2fBfb9P9Q==", + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/standard/-/standard-16.0.3.tgz", + "integrity": "sha512-70F7NH0hSkNXosXRltjSv6KpTAOkUkSfyu3ynyM5dtRUiLtR+yX9EGZ7RKwuGUqCJiX/cnkceVM6HTZ4JpaqDg==", "dev": true, "requires": { - "eslint": "~6.8.0", - "eslint-config-standard": "14.1.1", - "eslint-config-standard-jsx": "8.1.0", - "eslint-plugin-import": "~2.18.0", - "eslint-plugin-node": "~10.0.0", + "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.14.2", - "eslint-plugin-standard": "~4.0.0", - "standard-engine": "^12.0.0" + "eslint-plugin-react": "~7.21.5", + "standard-engine": "^14.0.1" } }, "standard-engine": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-12.1.0.tgz", - "integrity": "sha512-DVJnWM1CGkag4ucFLGdiYWa5/kJURPONmMmk17p8FT5NE4UnPZB1vxWnXnRo2sPSL78pWJG8xEM+1Tu19z0deg==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-14.0.1.tgz", + "integrity": "sha512-7FEzDwmHDOGva7r9ifOzD3BGdTbA7ujJ50afLVdW/tK14zQEptJjbFuUfn50irqdHDcTbNh0DTIoMPynMCXb0Q==", "dev": true, "requires": { - "deglob": "^4.0.1", - "get-stdin": "^7.0.0", + "get-stdin": "^8.0.0", "minimist": "^1.2.5", - "pkg-conf": "^3.1.0" + "pkg-conf": "^3.1.0", + "xdg-basedir": "^4.0.0" } }, "string-width": { @@ -2083,29 +2334,66 @@ "strip-ansi": "^3.0.0" } }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "string.prototype.matchall": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.3.tgz", + "integrity": "sha512-OBxYDA2ifZQ2e13cP82dWFMaCV9CGF8GzmN4fljBVw5O5wep0lu4gacm1OL6MjROoUnB8VbkWRThqkV2YFLNxw==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "es-abstract": "^1.18.0-next.1", + "has-symbols": "^1.0.1", + "internal-slot": "^1.0.2", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.3" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -2150,12 +2438,6 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, - "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 - }, "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", @@ -2190,39 +2472,36 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "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.2" + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" } }, - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", - "dev": true - }, "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.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "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.1.2" + "prelude-ls": "^1.2.1" } }, "type-fest": { @@ -2244,12 +2523,6 @@ "underscore": "^1.9.1" } }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, "uri-js": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", @@ -2260,9 +2533,9 @@ } }, "v8-compile-cache": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", - "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", "dev": true }, "validate-npm-package-license": { @@ -2276,9 +2549,9 @@ } }, "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -2297,7 +2570,7 @@ }, "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", @@ -2318,10 +2591,10 @@ "mkdirp": "^0.5.1" } }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "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": { @@ -2331,7 +2604,7 @@ }, "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", diff --git a/package.json b/package.json index 60699e43..9b9ea6fb 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { - "name": "snippets-dev", + "name": "snippets", "version": "2.0.0", "description": "Expand snippets matching the current prefix with `tab`.", "repository": "https://github.com/atom/snippets", "license": "MIT", - "main": "./snippets", + "main": "./snippets.js", "scripts": { - "generate": "pegjs -c ./lib/parser/config.js", + "generate": "pegjs -c ./parser/config.js", "lint": "standard", "lint:fix": "npm run lint -- --fix", "test": "npm run lint:fix && atom --test spec" @@ -17,23 +17,28 @@ "season": "^6.0.2" }, "devDependencies": { + "@babel/core": "^7.12.9", + "@babel/eslint-parser": "^7.12.1", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-private-methods": "^7.12.1", "mock-fs": "^4.13.0", "pegjs": "^0.11.0-master.b7b87ea", - "standard": "^14.3.4" + "standard": "^16.0.3" }, "engines": { - "atom": ">=1.47.0", - "node": ">=12.0.0" + "atom": ">=1.53.0", + "node": ">=12.4.0" }, "providedServices": { - "snippets-dev": { + "snippets": { "description": "Snippets are text shortcuts that can be expanded to their definition.", "versions": { - "0.2.0": "snippets" + "1.0.0": "snippets" } } }, "standard": { + "parser": "@babel/eslint-parser", "env": { "jasmine": true, "node": true diff --git a/parser/snippet-body-parser.js b/parser/snippet-body-parser.js index d2dab08f..b99cc52e 100644 --- a/parser/snippet-body-parser.js +++ b/parser/snippet-body-parser.js @@ -197,45 +197,44 @@ function peg$parse(input, options) { var peg$e24 = peg$literalExpectation("{", false); var peg$e25 = peg$classExpectation([["0", "9"], ["a", "f"]], false, true); - var peg$f0 = function(body) { return new Snippet(body, legacySyntax) }; - var peg$f1 = function(construct) { return new Modifier().create(construct) }; - var peg$f2 = function(construct, modifier) { return modifier.create(construct) }; - var peg$f3 = function(identifier) { return [Tabstop, identifier] }; - var peg$f4 = function(identifier) { return [Variable, identifier] }; - var peg$f5 = function(char) { return /[\p{L}\d_]/u.test(char) }; - var peg$f6 = function(choices) { return new Choice(choices) }; - var peg$f7 = function(content) { return new Placeholder(content) }; - var peg$f8 = function(transformation) { return new Transformation(transformation) }; - var peg$f9 = function() { return new Modifier() }; - var peg$f10 = function(first, rest) { return [first, ...rest] }; - var peg$f11 = function() { return escape('$,|') }; - var peg$f12 = function() { /*{*/ return escape('$}') }; - var peg$f13 = function() { return escapes.push('/') }; - var peg$f14 = function() { return escape('\\(/') }; - var peg$f15 = function() { return [''] }; - var peg$f16 = function(group, insert, replacement) { + var peg$f0 = function(body) { return new Snippet({ body, legacySyntax }) }; + var peg$f1 = function(construct, modifier) { return modifier.modify(construct) }; + var peg$f2 = function(identifier) { return new Tabstop({ identifier }) }; + var peg$f3 = function(identifier) { return new Variable({ identifier }) }; + var peg$f4 = function(char) { return /[\p{L}\d_]/u.test(char) }; + var peg$f5 = function(choices) { return new Choice(choices) }; + var peg$f6 = function(content) { return new Placeholder(content) }; + var peg$f7 = function(transformation) { return new Transformation(transformation) }; + var peg$f8 = function() { return new Modifier() }; + var peg$f9 = function(first, rest) { return [...rest, first] }; + var peg$f10 = function() { return escape('$,|') }; + var peg$f11 = function() { /*{*/ return escape('$}') }; + var peg$f12 = function() { return escapes.push('/') }; + var peg$f13 = function() { return escape('\\(/') }; + var peg$f14 = function() { return [''] }; + var peg$f15 = function(group, insert, replacement) { return { group: `$${group || '$'}`, insert, replacement } }; - var peg$f17 = function() { return escape(':)') }; - var peg$f18 = function() { return sequence => sequence }; - var peg$f19 = function() { return upperFirst }; - var peg$f20 = function() { return lowerFirst }; - var peg$f21 = function() { return toUpper }; - var peg$f22 = function() { return toLower }; - var peg$f23 = function(int) { return Number.parseInt(int) }; - var peg$f24 = function(char) { return !escaped(char) }; - var peg$f25 = function(chars) { return chars.join('') }; - var peg$f26 = function() { return escapes.pop() }; - var peg$f27 = function() { return '\n' }; - var peg$f28 = function() { return '\r' }; - var peg$f29 = function() { return '\v' }; - var peg$f30 = function() { return '\t' }; - var peg$f31 = function() { return '\b' }; - var peg$f32 = function() { return '\f' }; - var peg$f33 = function(codepoint) { + var peg$f16 = function() { return escape(':)') }; + var peg$f17 = function() { return sequence => sequence }; + var peg$f18 = function() { return upperFirst }; + var peg$f19 = function() { return lowerFirst }; + var peg$f20 = function() { return toUpper }; + var peg$f21 = function() { return toLower }; + var peg$f22 = function(int) { return Number.parseInt(int) }; + var peg$f23 = function(char) { return !escaped(char) }; + var peg$f24 = function(chars) { return chars.join('') }; + var peg$f25 = function() { return escapes.pop() }; + var peg$f26 = function() { return '\n' }; + var peg$f27 = function() { return '\r' }; + var peg$f28 = function() { return '\v' }; + var peg$f29 = function() { return '\t' }; + var peg$f30 = function() { return '\b' }; + var peg$f31 = function() { return '\f' }; + var peg$f32 = function(codepoint) { return String.fromCodePoint(Number.parseInt(codepoint, 16)) }; - var peg$f34 = function(char) { return escaped(char) }; + var peg$f33 = function(char) { return escaped(char) }; var peg$currPos = 0; var peg$savedPos = 0; @@ -477,8 +476,7 @@ function peg$parse(input, options) { if (s1 !== peg$FAILED) { s2 = peg$parseConstruct(); if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s0 = peg$f1(s2); + s0 = s2; } else { peg$currPos = s0; s0 = peg$FAILED; @@ -510,7 +508,7 @@ function peg$parse(input, options) { } if (s4 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f2(s2, s3); + s0 = peg$f1(s2, s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -543,7 +541,7 @@ function peg$parse(input, options) { s1 = peg$parseInt(); if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f3(s1); + s1 = peg$f2(s1); } s0 = s1; if (s0 === peg$FAILED) { @@ -551,7 +549,7 @@ function peg$parse(input, options) { s1 = peg$parseVariable(); if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f4(s1); + s1 = peg$f3(s1); } s0 = s1; } @@ -578,7 +576,7 @@ function peg$parse(input, options) { } if (s3 !== peg$FAILED) { peg$savedPos = peg$currPos; - s4 = peg$f5(s3); + s4 = peg$f4(s3); if (s4) { s4 = undefined; } else { @@ -608,7 +606,7 @@ function peg$parse(input, options) { } if (s3 !== peg$FAILED) { peg$savedPos = peg$currPos; - s4 = peg$f5(s3); + s4 = peg$f4(s3); if (s4) { s4 = undefined; } else { @@ -649,7 +647,7 @@ function peg$parse(input, options) { s1 = peg$parseChoice(); if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f6(s1); + s1 = peg$f5(s1); } s0 = s1; if (s0 === peg$FAILED) { @@ -657,7 +655,7 @@ function peg$parse(input, options) { s1 = peg$parsePlaceholder(); if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f7(s1); + s1 = peg$f6(s1); } s0 = s1; if (s0 === peg$FAILED) { @@ -665,14 +663,14 @@ function peg$parse(input, options) { s1 = peg$parseTransformation(); if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f8(s1); + s1 = peg$f7(s1); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; s1 = ''; peg$savedPos = s0; - s1 = peg$f9(); + s1 = peg$f8(); s0 = s1; } } @@ -752,7 +750,7 @@ function peg$parse(input, options) { } if (s4 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f10(s2, s3); + s0 = peg$f9(s2, s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -778,7 +776,7 @@ function peg$parse(input, options) { s0 = peg$currPos; peg$savedPos = peg$currPos; - s1 = peg$f11(); + s1 = peg$f10(); if (s1) { s1 = undefined; } else { @@ -823,7 +821,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = peg$currPos; - s2 = peg$f12(); + s2 = peg$f11(); if (s2) { s2 = undefined; } else { @@ -945,7 +943,7 @@ function peg$parse(input, options) { s0 = peg$currPos; peg$savedPos = peg$currPos; - s1 = peg$f13(); + s1 = peg$f12(); if (s1) { s1 = undefined; } else { @@ -982,7 +980,7 @@ function peg$parse(input, options) { s0 = peg$currPos; peg$savedPos = peg$currPos; - s1 = peg$f14(); + s1 = peg$f13(); if (s1) { s1 = undefined; } else { @@ -1031,7 +1029,7 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = ''; peg$savedPos = s0; - s1 = peg$f15(); + s1 = peg$f14(); s0 = s1; } @@ -1071,7 +1069,7 @@ function peg$parse(input, options) { } if (s5 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f16(s2, s3, s4); + s0 = peg$f15(s2, s3, s4); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1109,7 +1107,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = peg$currPos; - s2 = peg$f17(); + s2 = peg$f16(); if (s2) { s2 = undefined; } else { @@ -1158,7 +1156,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f18(); + s1 = peg$f17(); } s0 = s1; if (s0 === peg$FAILED) { @@ -1172,7 +1170,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f19(); + s1 = peg$f18(); } s0 = s1; if (s0 === peg$FAILED) { @@ -1186,7 +1184,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f20(); + s1 = peg$f19(); } s0 = s1; if (s0 === peg$FAILED) { @@ -1200,7 +1198,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f21(); + s1 = peg$f20(); } s0 = s1; if (s0 === peg$FAILED) { @@ -1214,7 +1212,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f22(); + s1 = peg$f21(); } s0 = s1; } @@ -1263,7 +1261,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f23(s1); + s1 = peg$f22(s1); } s0 = s1; @@ -1298,7 +1296,7 @@ function peg$parse(input, options) { } if (s3 !== peg$FAILED) { peg$savedPos = peg$currPos; - s4 = peg$f24(s3); + s4 = peg$f23(s3); if (s4) { s4 = undefined; } else { @@ -1337,7 +1335,7 @@ function peg$parse(input, options) { } if (s3 !== peg$FAILED) { peg$savedPos = peg$currPos; - s4 = peg$f24(s3); + s4 = peg$f23(s3); if (s4) { s4 = undefined; } else { @@ -1360,7 +1358,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f25(s1); + s1 = peg$f24(s1); } s0 = s1; @@ -1375,7 +1373,7 @@ function peg$parse(input, options) { } peg$savedPos = peg$currPos; - s0 = peg$f26(); + s0 = peg$f25(); if (s0) { s0 = undefined; } else { @@ -1402,7 +1400,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f27(); + s1 = peg$f26(); } s0 = s1; if (s0 === peg$FAILED) { @@ -1416,7 +1414,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f28(); + s1 = peg$f27(); } s0 = s1; if (s0 === peg$FAILED) { @@ -1430,7 +1428,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f29(); + s1 = peg$f28(); } s0 = s1; if (s0 === peg$FAILED) { @@ -1444,7 +1442,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f30(); + s1 = peg$f29(); } s0 = s1; if (s0 === peg$FAILED) { @@ -1458,7 +1456,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f31(); + s1 = peg$f30(); } s0 = s1; if (s0 === peg$FAILED) { @@ -1472,7 +1470,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f32(); + s1 = peg$f31(); } s0 = s1; if (s0 === peg$FAILED) { @@ -1491,7 +1489,7 @@ function peg$parse(input, options) { } if (s2 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f33(s2); + s0 = peg$f32(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1519,7 +1517,7 @@ function peg$parse(input, options) { } if (s2 !== peg$FAILED) { peg$savedPos = peg$currPos; - s3 = peg$f34(s2); + s3 = peg$f33(s2); if (s3) { s3 = undefined; } else { diff --git a/parser/snippet-body-parser.pegjs b/parser/snippet-body-parser.pegjs index 05ea2af2..9d308274 100644 --- a/parser/snippet-body-parser.pegjs +++ b/parser/snippet-body-parser.pegjs @@ -22,12 +22,12 @@ Test escape sequences ... Snippet = body:(Expression / String)+ { return new Snippet(body, legacySyntax) } Expression - = "$" construct:Construct { return new Modifier().create(construct) } - / "${" construct:Construct modifier:Modifier "}" { return modifier.create(construct) } + = "$" @Construct + / "${" construct:Construct modifier:Modifier "}" { return modifier.modify(construct) } Construct - = identifier:Tabstop { return [Tabstop, identifier] } - / identifier:Variable { return [Variable, identifier] } + = identifier:Tabstop { return new Tabstop(identifier) } + / identifier:Variable { return new Variable(identifier) } Tabstop = Int @@ -39,7 +39,7 @@ Modifier / transformation:Transformation { return new Transformation(transformation) } / "" { return new Modifier() } -Choice = "|" first:Selection rest:("," @Selection)* "|" { return [first, ...rest] } +Choice = "|" first:Selection rest:("," @Selection)* "|" { return [...rest, first] } Selection = & { return escape('$,|') } @Snippet EOL diff --git a/snippets-available.js b/snippets-available.js deleted file mode 100644 index 3c3e92d7..00000000 --- a/snippets-available.js +++ /dev/null @@ -1,83 +0,0 @@ - -const _ = require('underscore-plus') -const SelectListView = require('atom-select-list') - -module.exports = 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 (const 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/snippets.js b/snippets.js index 26bdb957..22e49d03 100644 --- a/snippets.js +++ b/snippets.js @@ -1,129 +1,144 @@ const { CompositeDisposable, File } = require('atom') -const season = require('season') +const CSON = require('season') const path = require('path') -const fs = require('fs') +const fs = require('fs').promises 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 async activate () { - this.disposables = new CompositeDisposable() - this.snippetsByScopes = new ScopedPropertyStore() - this.packageDisposables = new WeakMap() - - this.disposables.add( - { dispose: () => delete this.loaded }, - atom.workspace.addOpener(uri => uri === 'atom://.atom/snippets' - ? atom.workspace.openTextFile(this.userSnippetsPath) - : undefined), - atom.commands.add('atom-text-editor', 'snippets:available', () => - this.availableSnippetsView.toggle(atom.workspace.getActiveTextEditor())), - atom.packages.onDidActivatePackage(pack => this.loadPackage(pack)), - atom.packages.onDidDeactivatePackage(pack => this.unloadPackage(pack))) - - await (this.loaded = Promise.all([ - this.loadUserSnippets(), - ...atom.packages.getActivePackages().map(pack => this.loadPackage(pack)) - ]).then(() => true)) - } + 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 get availableSnippetsView () { - delete this.availableSnippetsView + static #userSnippetsFile + static #userSnippetsBasename = path.join(atom.getConfigDirPath(), 'snippets') - const SnippetsAvailable = require('./snippets-available') - return (this.availableSnippetsView = new SnippetsAvailable(this)) - } + static #userSnippetsURI = 'atom://.atom/snippets' - static get userSnippetsPath () { - let userSnippetsPath = path.join(atom.getConfigDirPath(), 'snippets.json') - try { - fs.accessSync(this.userSnippetsPath) - } catch (error) { - userSnippetsPath = path.join(userSnippetsPath, '../snippets.cson') + // 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() } - return userSnippetsPath } - static loadSnippetsFile (filepath) { - const priority = filepath === this.userSnippetsPath ? 1000 : 0 - return new Promise((resolve, reject) => - season.readFile(filepath, (error, object) => error == null - ? resolve(this.snippetsByScopes.addProperties(filepath, object, { priority })) - : reject(error))) + 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 async loadUserSnippets () { - const userSnippetsPath = this.userSnippetsPath + static #readSnippets = async (filepath) => { try { - const userSnippetsFile = new File(userSnippetsPath) - // Allow user defined snippets to be reloaded - this.unloadPackage(this) - this.packageDisposables.set(this, new CompositeDisposable( - await this.loadSnippetsFile(userSnippetsPath), - userSnippetsFile.onDidChange(() => this.loadUserSnippets()), - userSnippetsFile.onDidDelete(() => this.loadUserSnippets()), - userSnippetsFile.onDidRename(() => this.loadUserSnippets()))) + 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: '${userSnippetsPath}'`, { + 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 {} } } - static async loadPackage (pack) { - const snippetsDirectory = path.join(pack.path, 'snippets') + // 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.promises.readdir(snippetsDirectory) - files.forEach(async file => { - const snippetsFile = path.join(snippetsDirectory, file) - try { - const disposable = await this.loadSnippetsFile(snippetsFile) - this.packageDisposables.has(pack) - ? this.packageDisposables.get(pack).add(disposable) - : this.packageDisposables.set(pack, new CompositeDisposable(disposable)) - } catch (error) { - atom.notifications.addWarning(`Unable to load snippets from: '${snippetsFile}'`, { - description: 'Make sure you have permissions to access the directory and file.', - detail: error.toString(), - dismissable: true - }) - } - }) + 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 ${snippetsDirectory}`, { - description: 'Make sure you have permissions to access the directory and file.', + 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 unloadPackage (pack) { - if (this.packageDisposables.has(pack)) { - this.packageDisposables.get(pack).dispose() - this.packageDisposables.delete(pack) - } - } - - static snippets () { - return { - parse: string => parser.parse(string), - userSnippetsPath: () => this.userSnippetsPath, - snippetsByScopes: () => this.snippetsByScopes, - loaded: () => this.loaded || Promise.resolve(false) - } - } - - static get deactivate () { - return this.disposables.dispose + 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))) } } From 1b23a6388c5f5e894c4626cd48f7ffa0373fb403 Mon Sep 17 00:00:00 2001 From: Fluvf <14368815+Fluvf@users.noreply.github.com> Date: Sat, 17 Apr 2021 08:13:31 +0300 Subject: [PATCH 17/17] almost done now i made some refactoring and such this is more of a "hey, this is till being worked on" commit stuff still to do: - transformations are not done, i changed the format (easy) - tests are incomplete (being worked on, not on this commit) (easy but time consuming) - need to go over things and document them properly (hard, but i think i might have something to make it easier) --- available-snippets-view.js | 11 +- constructs/tabstop.js | 7 - constructs/variable.js | 9 - {constructs => expression}/babel.config.json | 0 expression/choice.js | 60 + .../construct.js => expression/expression.js | 19 +- expression/placeholder.js | 21 + {constructs => expression}/snippet.js | 28 +- expression/transformation.js | 40 + modifiers/babel.config.json | 6 - modifiers/choice.js | 66 - modifiers/modifier.js | 6 - modifiers/placeholder.js | 29 - modifiers/transformation.js | 46 - package-lock.json | 1271 +++++++---------- package.json | 15 +- parser/config.js | 12 +- parser/snippet-body-parser.js | 974 ++++++------- parser/snippet-body-parser.pegjs | 76 +- snippets.js | 3 +- 20 files changed, 1110 insertions(+), 1589 deletions(-) delete mode 100644 constructs/tabstop.js delete mode 100644 constructs/variable.js rename {constructs => expression}/babel.config.json (100%) create mode 100644 expression/choice.js rename constructs/construct.js => expression/expression.js (50%) create mode 100644 expression/placeholder.js rename {constructs => expression}/snippet.js (90%) create mode 100644 expression/transformation.js delete mode 100644 modifiers/babel.config.json delete mode 100644 modifiers/choice.js delete mode 100644 modifiers/modifier.js delete mode 100644 modifiers/placeholder.js delete mode 100644 modifiers/transformation.js diff --git a/available-snippets-view.js b/available-snippets-view.js index d73b4771..fc476d6b 100644 --- a/available-snippets-view.js +++ b/available-snippets-view.js @@ -6,19 +6,18 @@ module.exports = class AvailableSnippetsView extends SelectListView { items: Object.entries(snippets.snippetsByScopes() .getPropertyValue(editor.getRootScopeDescriptor().getScopeChain())), filterKeyForItem: ([name, { prefix }]) => prefix + name, - elementForItem: ([name, { prefix }]) => { + elementForItem: ([name, { prefix, description }]) => { const li = document.createElement('li') li.classList.add('two-lines') - const primaryLine = document.createElement('div') + const primaryLine = li.appendChild(document.createElement('div')) primaryLine.classList.add('primary-line') primaryLine.textContent = prefix - li.appendChild(primaryLine) - const secondaryLine = document.createElement('div') + const secondaryLine = li.appendChild(document.createElement('div')) secondaryLine.classList.add('secondary-line') - secondaryLine.textContent = name - li.appendChild(secondaryLine) + // TODO: Nullish coalescing operator + secondaryLine.textContent = description != null ? description : name return li }, diff --git a/constructs/tabstop.js b/constructs/tabstop.js deleted file mode 100644 index a85ab6e1..00000000 --- a/constructs/tabstop.js +++ /dev/null @@ -1,7 +0,0 @@ -const Construct = require('./construct') - -module.exports = class Tabstop extends Construct { - expand (editor, cursor, tabstops, variables) { - this.mark({ tabstops, start: cursor.getBufferPosition() }) - } -} diff --git a/constructs/variable.js b/constructs/variable.js deleted file mode 100644 index de45b393..00000000 --- a/constructs/variable.js +++ /dev/null @@ -1,9 +0,0 @@ -const Construct = require('./construct') - -module.exports = class Variable extends Construct { - expand (editor, cursor, tabstops, variables) { - this.identifier in variables - ? this.insert(editor, cursor, variables[this.identifier]) - : this.mark({ tabstops, ...this.insert(editor, cursor, this.identifier) }) - } -} diff --git a/constructs/babel.config.json b/expression/babel.config.json similarity index 100% rename from constructs/babel.config.json rename to expression/babel.config.json 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/constructs/construct.js b/expression/expression.js similarity index 50% rename from constructs/construct.js rename to expression/expression.js index 60156b8f..92367ac0 100644 --- a/constructs/construct.js +++ b/expression/expression.js @@ -1,9 +1,20 @@ -module.exports = class Construct { +module.exports = class Expression { constructor (identifier) { this.identifier = identifier } - expand () {} + 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) @@ -24,8 +35,8 @@ module.exports = class Construct { } } - mark ({ tabstops, start, end = start, exclusive = true, construct = this }) { - tabstops.markBufferRange({ start, end }, { exclusive }).setProperties({ construct }) + mark ({ tabstops, start, end = start, exclusive = true, expression = this }) { + tabstops.markBufferRange({ start, end }, { exclusive }).setProperties({ expression }) } toString () { 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/constructs/snippet.js b/expression/snippet.js similarity index 90% rename from constructs/snippet.js rename to expression/snippet.js index 474d302a..2dec54e9 100644 --- a/constructs/snippet.js +++ b/expression/snippet.js @@ -1,9 +1,9 @@ const { CompositeDisposable } = require('atom') const path = require('path') -const Construct = require('./construct') +const Expression = require('./expression') -module.exports = class Snippet extends Construct { +module.exports = class Snippet extends Expression { static VARIABLES = { // The currently selected text or the empty string TM_SELECTED_TEXT: (editor, cursor) => cursor.selection.getText(), @@ -61,9 +61,10 @@ module.exports = class Snippet extends Construct { // 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]()) + ? (cache[property] = this.VARIABLES[property](editor, cursor)) : property in custom ? custom[property] // We should never see this value used @@ -77,12 +78,12 @@ module.exports = class Snippet extends Construct { const unknowns = [] markers.forEach(marker => { - const { construct } = marker.getProperties() + const { expression } = marker.getProperties() - Number.isInteger(construct.identifier) - ? Array.isArray(tabstops[construct.identifier]) - ? tabstops[construct.identifier].push(marker) - : tabstops[construct.identifier] = [marker] + 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 @@ -95,13 +96,11 @@ module.exports = class Snippet extends Construct { return tabstops } - constructor (body, legacySyntax) { + constructor (body) { // This snippet will work as the default ending tabstop super(0) this.body = body - - this.legacySyntax = legacySyntax } // We work as the default ending tabstop, this is a special case @@ -109,7 +108,6 @@ module.exports = class Snippet extends Construct { cursor.setBufferPosition(stop.getBufferRange().end) } - // helper cause Snippet isn't really ever available expand ({ editor = atom.workspace.getActiveTextEditor(), cursor = editor.getLastCursor(), @@ -123,7 +121,7 @@ module.exports = class Snippet extends Construct { }) } - // Construct a variable proxy to access given and built-in variables + // 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 @@ -160,8 +158,8 @@ module.exports = class Snippet extends Construct { const iteration = this.iterator.next() if (!iteration.done) { const { value: [stop] } = iteration - const { construct } = stop.getProperties() - iteration.value.forEach(mirror => construct.activate(editor, cursor, stop, mirror)) + const { expression } = stop.getProperties() + iteration.value.forEach(mirror => expression.activate(editor, cursor, stop, mirror)) return true } disposables.dispose() 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/modifiers/babel.config.json b/modifiers/babel.config.json deleted file mode 100644 index f0525c14..00000000 --- a/modifiers/babel.config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "plugins": [ - "@babel/plugin-proposal-class-properties", - "@babel/plugin-proposal-private-methods" - ] -} diff --git a/modifiers/choice.js b/modifiers/choice.js deleted file mode 100644 index 40220129..00000000 --- a/modifiers/choice.js +++ /dev/null @@ -1,66 +0,0 @@ -const { CompositeDisposable } = require('atom') - -const Modifier = require('./modifier') - -module.exports = class Choice extends Modifier { - constructor (choices) { - super() - - 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] - } - - modify (construct) { - class Choice extends construct.constructor { - 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() - } - } - - return Object.assign(new Choice(construct), this) - } -} diff --git a/modifiers/modifier.js b/modifiers/modifier.js deleted file mode 100644 index 879235dc..00000000 --- a/modifiers/modifier.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = class Modifier { - modify (construct) { - // Don't actually modify the construct - return construct - } -} diff --git a/modifiers/placeholder.js b/modifiers/placeholder.js deleted file mode 100644 index a9fbae04..00000000 --- a/modifiers/placeholder.js +++ /dev/null @@ -1,29 +0,0 @@ -const Modifier = require('./modifier') - -module.exports = class Placeholder extends Modifier { - constructor (snippet) { - super() - - this.snippet = snippet - // Set to false, so the notification doesn't get created multiple times - this.snippet.legacySyntax = false - } - - modify (construct) { - class Placeholder extends construct.constructor { - 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() - } - } - - return Object.assign(new Placeholder(construct), this) - } -} diff --git a/modifiers/transformation.js b/modifiers/transformation.js deleted file mode 100644 index 0f7500da..00000000 --- a/modifiers/transformation.js +++ /dev/null @@ -1,46 +0,0 @@ -const Modifier = require('./modifier') - -module.exports = class Transformation extends Modifier { - constructor ([regexp, format, flags]) { - super() - - this.regexp = new RegExp(regexp, flags.join('')) - this.format = format - } - - modify (construct) { - class Transformation extends construct.constructor { - 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()) - } - } - - return Object.assign(new Transformation(construct), this) - } -} diff --git a/package-lock.json b/package-lock.json index 0ab8423e..cd9448e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,332 +5,46 @@ "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "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.10.4" - } - }, - "@babel/core": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", - "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.5", - "@babel/parser": "^7.12.7", - "@babel/template": "^7.12.7", - "@babel/traverse": "^7.12.9", - "@babel/types": "^7.12.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "@babel/highlight": "^7.12.13" } }, "@babel/eslint-parser": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.12.1.tgz", - "integrity": "sha512-cc7WQHnHQY3++/bghgbDtPx+5bf6xTsokyGzV6Qzh65NLz/unv+mPQuACkQ9GFhIhcTFv6yqwNaEcfX7EkOEsg==", + "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-scope": "^5.1.0", "eslint-visitor-keys": "^1.3.0", "semver": "^6.3.0" - }, - "dependencies": { - "eslint-scope": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", - "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "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 - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", - "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", - "dev": true, - "requires": { - "@babel/types": "^7.12.5", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", - "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", - "dev": true, - "requires": { - "@babel/types": "^7.12.7" - } - }, - "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.5" - } - }, - "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "lodash": "^4.17.19" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.7.tgz", - "integrity": "sha512-I5xc9oSJ2h59OwyUqjv95HRyzxj53DAubUERgQMrpcCEYQyToeHA+NEcUEsVWB4j53RDeskeBJ0SgRAYHDBckw==", - "dev": true, - "requires": { - "@babel/types": "^7.12.7" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", - "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.12.5", - "@babel/types": "^7.12.5" - } - }, - "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" } }, "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "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/helpers": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", - "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", - "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.5", - "@babel/types": "^7.12.5" - } - }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "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.10.4", + "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", "js-tokens": "^4.0.0" - }, - "dependencies": { - "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" - } - } - } - }, - "@babel/parser": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz", - "integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==", - "dev": true - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", - "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", - "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" - } - }, - "@babel/traverse": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz", - "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - }, - "dependencies": { - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" } }, "@eslint/eslintrc": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz", - "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==", + "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", @@ -343,6 +57,17 @@ "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": { @@ -382,9 +107,10 @@ "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", @@ -405,13 +131,15 @@ } }, "array-includes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", - "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "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.17.0", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", "is-string": "^1.0.5" } }, @@ -424,28 +152,6 @@ "call-bind": "^1.0.0", "define-properties": "^1.1.3", "es-abstract": "^1.18.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } } }, "array.prototype.flatmap": { @@ -458,28 +164,6 @@ "define-properties": "^1.1.3", "es-abstract": "^1.18.0-next.1", "function-bind": "^1.1.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } } }, "astral-regex": { @@ -494,11 +178,11 @@ "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" } }, @@ -508,9 +192,9 @@ "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.11", @@ -522,13 +206,13 @@ } }, "call-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", - "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "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.0" + "get-intrinsic": "^1.0.2" } }, "callsites": { @@ -543,71 +227,69 @@ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "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": "^4.1.0", - "supports-color": "^7.1.0" + "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", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" }, "dependencies": { - "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, + "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": { - "color-convert": "^2.0.1" + "number-is-nan": "^1.0.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, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "color-name": "~1.1.4" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, - "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 - }, - "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 - }, - "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, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "has-flag": "^4.0.0" + "ansi-regex": "^2.0.0" } } } }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, + "coffee-script": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", + "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==" + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -634,15 +316,6 @@ "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", "dev": true }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -654,6 +327,14 @@ "which": "^2.0.1" } }, + "cson-parser": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-1.3.5.tgz", + "integrity": "sha1-fsZ14DkUVTO/KmqFYHPxWZ2cLSQ=", + "requires": { + "coffee-script": "^1.10.0" + } + }, "d": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", @@ -736,22 +417,27 @@ } }, "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "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.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", + "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.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.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": { @@ -886,19 +572,77 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "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 }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "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": { - "ansi-regex": "^5.0.0" + "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" } } } @@ -1046,12 +790,6 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true } } }, @@ -1108,39 +846,23 @@ "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "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 - } } }, "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==", + "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.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", - "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", + "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.2.0", + "acorn-jsx": "^5.3.1", "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "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 - } } }, "esprima": { @@ -1150,9 +872,9 @@ "dev": true }, "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "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" @@ -1196,9 +918,9 @@ "dev": true }, "etch": { - "version": "0.12.8", - "resolved": "https://registry.npmjs.org/etch/-/etch-0.12.8.tgz", - "integrity": "sha512-dFLRe4wLroVtwzyy1vGlE3BSDZHiL0kZME5XgNGzZIULcYTvVno8vbiIleAesoKJmwWaxDTzG+4eppg2zk14JQ==" + "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", @@ -1217,9 +939,9 @@ }, "dependencies": { "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==" } } }, @@ -1268,17 +990,6 @@ "flatted": "^2.0.0", "rimraf": "2.6.3", "write": "1.0.3" - }, - "dependencies": { - "rimraf": { - "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.1.3" - } - } } }, "flatted": { @@ -1296,6 +1007,16 @@ "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": { @@ -1320,16 +1041,10 @@ "resolved": "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz", "integrity": "sha1-kCBMPi/appQbso0WZF1BgGOpDps=" }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, "get-intrinsic": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", - "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "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", @@ -1357,27 +1072,18 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "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" } }, - "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" - } - }, "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, "grim": { @@ -1397,6 +1103,12 @@ "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", @@ -1404,15 +1116,15 @@ "dev": true }, "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "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.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "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": { @@ -1422,9 +1134,9 @@ "dev": true }, "import-fresh": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", - "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "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", @@ -1452,14 +1164,14 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "internal-slot": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.2.tgz", - "integrity": "sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g==", + "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": { - "es-abstract": "^1.17.0-next.1", + "get-intrinsic": "^1.1.0", "has": "^1.0.3", - "side-channel": "^1.0.2" + "side-channel": "^1.0.4" } }, "invert-kv": { @@ -1473,16 +1185,31 @@ "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.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "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.1.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", - "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", + "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" @@ -1501,12 +1228,10 @@ "dev": true }, "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" - } + "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", @@ -1518,17 +1243,24 @@ } }, "is-negative-zero": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "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.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "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" } }, @@ -1566,21 +1298,15 @@ "dev": true }, "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "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" } }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, "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", @@ -1599,23 +1325,14 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "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" - } - }, "jsx-ast-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.1.0.tgz", - "integrity": "sha512-d4/UOjg+mxAWxCiF0c5UTSwyqbchkbqCvK87aBovhnh8GtysTjWmgC63tY0cJx/HzGgm9qnA147jVBdpOiQ2RA==", + "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.1", - "object.assign": "^4.1.1" + "array-includes": "^3.1.2", + "object.assign": "^4.1.2" } }, "key-path-helpers": { @@ -1664,9 +1381,9 @@ } }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "loose-envify": { @@ -1678,6 +1395,15 @@ "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", @@ -1759,9 +1485,9 @@ "dev": true }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "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": { @@ -1783,37 +1509,38 @@ } }, "object.entries": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz", - "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==", + "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.17.5", + "es-abstract": "^1.18.0-next.1", "has": "^1.0.3" } }, "object.fromentries": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz", - "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==", + "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.17.0-next.1", - "function-bind": "^1.1.1", + "es-abstract": "^1.18.0-next.2", "has": "^1.0.3" } }, "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "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.17.0-next.1", - "function-bind": "^1.1.1", + "es-abstract": "^1.18.0-next.2", "has": "^1.0.3" } }, @@ -2098,13 +1825,13 @@ } }, "regexp.prototype.flags": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", - "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "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": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "regexpp": { @@ -2114,12 +1841,12 @@ "dev": true }, "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "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.1.0", + "is-core-module": "^2.2.0", "path-parse": "^1.0.6" } }, @@ -2130,19 +1857,14 @@ "dev": true }, "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "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.1.3" } }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, "scoped-property-store": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/scoped-property-store/-/scoped-property-store-0.17.0.tgz", @@ -2163,27 +1885,12 @@ "cson-parser": "^1.3.0", "fs-plus": "^3.0.0", "yargs": "^3.23.0" - }, - "dependencies": { - "coffee-script": { - "version": "1.12.7", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", - "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==" - }, - "cson-parser": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-1.3.5.tgz", - "integrity": "sha1-fsZ14DkUVTO/KmqFYHPxWZ2cLSQ=", - "requires": { - "coffee-script": "^1.10.0" - } - } } }, "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "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": { @@ -2202,35 +1909,14 @@ "dev": true }, "side-channel": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.3.tgz", - "integrity": "sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "dev": true, "requires": { - "es-abstract": "^1.18.0-next.0", - "object-inspect": "^1.8.0" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" } }, "slice-ansi": { @@ -2242,22 +1928,8 @@ "ansi-styles": "^3.2.0", "astral-regex": "^1.0.0", "is-fullwidth-code-point": "^2.0.0" - }, - "dependencies": { - "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 - } } }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -2285,9 +1957,9 @@ } }, "spdx-license-ids": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", - "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", + "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": { @@ -2325,78 +1997,75 @@ } }, "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" - } - }, - "string.prototype.matchall": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.3.tgz", - "integrity": "sha512-OBxYDA2ifZQ2e13cP82dWFMaCV9CGF8GzmN4fljBVw5O5wep0lu4gacm1OL6MjROoUnB8VbkWRThqkV2YFLNxw==", + "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": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "has-symbols": "^1.0.1", - "internal-slot": "^1.0.2", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.3" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" }, "dependencies": { - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "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": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" + "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.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", - "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "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.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", - "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "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": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://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": { @@ -2430,40 +2099,6 @@ "lodash": "^4.17.14", "slice-ansi": "^2.1.0", "string-width": "^3.0.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 - }, - "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 - }, - "string-width": { - "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" - } - }, - "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" - } - } } }, "text-table": { @@ -2472,12 +2107,6 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, "tsconfig-paths": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", @@ -2488,6 +2117,17 @@ "json5": "^1.0.1", "minimist": "^1.2.0", "strip-bom": "^3.0.0" + }, + "dependencies": { + "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": { @@ -2510,10 +2150,22 @@ "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.11.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.11.0.tgz", - "integrity": "sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw==" + "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.7.0", @@ -2524,18 +2176,18 @@ } }, "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "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.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "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": { @@ -2557,6 +2209,19 @@ "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": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", @@ -2575,6 +2240,39 @@ "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": { @@ -2598,9 +2296,15 @@ "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", @@ -2614,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 9b9ea6fb..1b1cbd80 100644 --- a/package.json +++ b/package.json @@ -9,25 +9,22 @@ "generate": "pegjs -c ./parser/config.js", "lint": "standard", "lint:fix": "npm run lint -- --fix", - "test": "npm run lint:fix && atom --test spec" + "test": "atom --test specs" }, "dependencies": { - "atom-select-list": "^0.7.2", + "atom-select-list": "^0.8.0", "scoped-property-store": "^0.17.0", "season": "^6.0.2" }, "devDependencies": { - "@babel/core": "^7.12.9", - "@babel/eslint-parser": "^7.12.1", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-proposal-private-methods": "^7.12.1", + "@babel/eslint-parser": "^7.13.14", "mock-fs": "^4.13.0", - "pegjs": "^0.11.0-master.b7b87ea", + "pegjs": "0.11.0-master.b7b87ea", "standard": "^16.0.3" }, "engines": { - "atom": ">=1.53.0", - "node": ">=12.4.0" + "atom": ">=1.56.0", + "node": ">=12.14.1" }, "providedServices": { "snippets": { diff --git a/parser/config.js b/parser/config.js index 26f8c802..8b62d5f5 100644 --- a/parser/config.js +++ b/parser/config.js @@ -2,12 +2,10 @@ module.exports = { input: './parser/snippet-body-parser.pegjs', output: './parser/snippet-body-parser.js', dependencies: { - Snippet: '../constructs/snippet', - Tabstop: '../constructs/tabstop', - Variable: '../constructs/variable', - Choice: '../modifiers/choice', - Modifier: '../modifiers/modifier', - Placeholder: '../modifiers/placeholder', - Transformation: '../modifiers/transformation' + 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 index b99cc52e..ca40945c 100644 --- a/parser/snippet-body-parser.js +++ b/parser/snippet-body-parser.js @@ -2,13 +2,11 @@ "use strict"; -var Snippet = require("../constructs/snippet"); -var Tabstop = require("../constructs/tabstop"); -var Variable = require("../constructs/variable"); -var Choice = require("../modifiers/choice"); -var Modifier = require("../modifiers/modifier"); -var Placeholder = require("../modifiers/placeholder"); -var Transformation = require("../modifiers/transformation"); +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; } @@ -150,25 +148,16 @@ function peg$parse(input, options) { var peg$c4 = ","; var peg$c5 = ":"; var peg$c6 = "/"; - var peg$c7 = "(?"; - var peg$c8 = ")"; - var peg$c9 = "\\E"; - var peg$c10 = "\\u"; - var peg$c11 = "\\l"; - var peg$c12 = "\\U"; - var peg$c13 = "\\L"; - var peg$c14 = "\\n"; - var peg$c15 = "\\r"; - var peg$c16 = "\\v"; - var peg$c17 = "\\t"; - var peg$c18 = "\\b"; - var peg$c19 = "\\f"; - var peg$c20 = "\\"; - var peg$c21 = "{"; + 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$r2 = /^[0-9a-f]/i; var peg$e0 = peg$literalExpectation("$", false); var peg$e1 = peg$literalExpectation("${", false); @@ -179,62 +168,39 @@ function peg$parse(input, options) { 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("(?", false); - var peg$e10 = peg$literalExpectation(")", false); - var peg$e11 = peg$literalExpectation("\\E", false); - var peg$e12 = peg$literalExpectation("\\u", false); - var peg$e13 = peg$literalExpectation("\\l", false); - var peg$e14 = peg$literalExpectation("\\U", false); - var peg$e15 = peg$literalExpectation("\\L", false); - var peg$e16 = peg$classExpectation([["0", "9"]], false, false); - var peg$e17 = peg$literalExpectation("\\n", false); - var peg$e18 = peg$literalExpectation("\\r", false); - var peg$e19 = peg$literalExpectation("\\v", false); - var peg$e20 = peg$literalExpectation("\\t", false); - var peg$e21 = peg$literalExpectation("\\b", false); - var peg$e22 = peg$literalExpectation("\\f", false); - var peg$e23 = peg$literalExpectation("\\", false); - var peg$e24 = peg$literalExpectation("{", false); - var peg$e25 = peg$classExpectation([["0", "9"], ["a", "f"]], false, true); - - var peg$f0 = function(body) { return new Snippet({ body, legacySyntax }) }; - var peg$f1 = function(construct, modifier) { return modifier.modify(construct) }; - var peg$f2 = function(identifier) { return new Tabstop({ identifier }) }; - var peg$f3 = function(identifier) { return new Variable({ identifier }) }; - var peg$f4 = function(char) { return /[\p{L}\d_]/u.test(char) }; - var peg$f5 = function(choices) { return new Choice(choices) }; - var peg$f6 = function(content) { return new Placeholder(content) }; - var peg$f7 = function(transformation) { return new Transformation(transformation) }; - var peg$f8 = function() { return new Modifier() }; - var peg$f9 = function(first, rest) { return [...rest, first] }; - var peg$f10 = function() { return escape('$,|') }; - var peg$f11 = function() { /*{*/ return escape('$}') }; - var peg$f12 = function() { return escapes.push('/') }; - var peg$f13 = function() { return escape('\\(/') }; - var peg$f14 = function() { return [''] }; - var peg$f15 = function(group, insert, replacement) { - return { group: `$${group || '$'}`, insert, replacement } - }; - var peg$f16 = function() { return escape(':)') }; - var peg$f17 = function() { return sequence => sequence }; - var peg$f18 = function() { return upperFirst }; - var peg$f19 = function() { return lowerFirst }; - var peg$f20 = function() { return toUpper }; - var peg$f21 = function() { return toLower }; - var peg$f22 = function(int) { return Number.parseInt(int) }; - var peg$f23 = function(char) { return !escaped(char) }; - var peg$f24 = function(chars) { return chars.join('') }; - var peg$f25 = function() { return escapes.pop() }; - var peg$f26 = function() { return '\n' }; - var peg$f27 = function() { return '\r' }; - var peg$f28 = function() { return '\v' }; - var peg$f29 = function() { return '\t' }; - var peg$f30 = function() { return '\b' }; - var peg$f31 = function() { return '\f' }; - var peg$f32 = function(codepoint) { - return String.fromCodePoint(Number.parseInt(codepoint, 16)) - }; - var peg$f33 = function(char) { return escaped(char) }; + 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; @@ -474,9 +440,13 @@ function peg$parse(input, options) { s1 = peg$FAILED; } if (s1 !== peg$FAILED) { - s2 = peg$parseConstruct(); + s2 = peg$parseInt(); + if (s2 === peg$FAILED) { + s2 = peg$parseVariable(); + } if (s2 !== peg$FAILED) { - s0 = s2; + peg$savedPos = s0; + s0 = peg$f1(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -495,24 +465,21 @@ function peg$parse(input, options) { s1 = peg$FAILED; } if (s1 !== peg$FAILED) { - s2 = peg$parseConstruct(); + s2 = peg$parseInt(); + if (s2 === peg$FAILED) { + s2 = peg$parseVariable(); + } if (s2 !== peg$FAILED) { - s3 = peg$parseModifier(); + rule$expects(peg$e2); + if (input.charCodeAt(peg$currPos) === 125) { + s3 = peg$c2; + peg$currPos++; + } else { + s3 = peg$FAILED; + } 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$f1(s2, s3); - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } + peg$savedPos = s0; + s0 = peg$f1(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -525,33 +492,132 @@ function peg$parse(input, options) { peg$currPos = s0; s0 = peg$FAILED; } - } - - return s0; - } - - function peg$parseConstruct() { - var s0, s1; - - var rule$expects = function (expected) { - if (peg$silentFails === 0) peg$expect(expected); - } - - s0 = peg$currPos; - s1 = peg$parseInt(); - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$f2(s1); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - s1 = peg$parseVariable(); - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$f3(s1); + 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; + } + } + } } - s0 = s1; } return s0; @@ -576,7 +642,7 @@ function peg$parse(input, options) { } if (s3 !== peg$FAILED) { peg$savedPos = peg$currPos; - s4 = peg$f4(s3); + s4 = peg$f5(s3); if (s4) { s4 = undefined; } else { @@ -606,7 +672,7 @@ function peg$parse(input, options) { } if (s3 !== peg$FAILED) { peg$savedPos = peg$currPos; - s4 = peg$f4(s3); + s4 = peg$f5(s3); if (s4) { s4 = undefined; } else { @@ -636,49 +702,6 @@ function peg$parse(input, options) { return s0; } - function peg$parseModifier() { - var s0, s1; - - var rule$expects = function (expected) { - if (peg$silentFails === 0) peg$expect(expected); - } - - s0 = peg$currPos; - s1 = peg$parseChoice(); - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$f5(s1); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - s1 = peg$parsePlaceholder(); - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$f6(s1); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - s1 = peg$parseTransformation(); - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$f7(s1); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - s1 = ''; - peg$savedPos = s0; - s1 = peg$f8(); - s0 = s1; - } - } - } - - return s0; - } - function peg$parseChoice() { var s0, s1, s2, s3, s4, s5, s6; @@ -750,7 +773,7 @@ function peg$parse(input, options) { } if (s4 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f9(s2, s3); + s0 = peg$f6(s2, s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -776,14 +799,14 @@ function peg$parse(input, options) { s0 = peg$currPos; peg$savedPos = peg$currPos; - s1 = peg$f10(); + s1 = peg$f7(); if (s1) { s1 = undefined; } else { s1 = peg$FAILED; } if (s1 !== peg$FAILED) { - s2 = peg$parseSnippet(); + s2 = peg$parseString(); if (s2 !== peg$FAILED) { s3 = peg$parseEOL(); if (s3 !== peg$FAILED) { @@ -821,7 +844,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = peg$currPos; - s2 = peg$f11(); + s2 = peg$f8(); if (s2) { s2 = undefined; } else { @@ -943,7 +966,7 @@ function peg$parse(input, options) { s0 = peg$currPos; peg$savedPos = peg$currPos; - s1 = peg$f12(); + s1 = peg$f9(); if (s1) { s1 = undefined; } else { @@ -980,7 +1003,7 @@ function peg$parse(input, options) { s0 = peg$currPos; peg$savedPos = peg$currPos; - s1 = peg$f13(); + s1 = peg$f10(); if (s1) { s1 = undefined; } else { @@ -988,22 +1011,16 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { s2 = []; - s3 = peg$parseConditionalInsert(); + s3 = peg$parseInsert(); if (s3 === peg$FAILED) { - s3 = peg$parseCaseFold(); - if (s3 === peg$FAILED) { - s3 = peg$parseString(); - } + s3 = peg$parseString(); } if (s3 !== peg$FAILED) { while (s3 !== peg$FAILED) { s2.push(s3); - s3 = peg$parseConditionalInsert(); + s3 = peg$parseInsert(); if (s3 === peg$FAILED) { - s3 = peg$parseCaseFold(); - if (s3 === peg$FAILED) { - s3 = peg$parseString(); - } + s3 = peg$parseString(); } } } else { @@ -1029,47 +1046,61 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = ''; peg$savedPos = s0; - s1 = peg$f14(); + s1 = peg$f11(); s0 = s1; } return s0; } - function peg$parseConditionalInsert() { - var s0, s1, s2, s3, s4, s5; + 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$e9); - if (input.substr(peg$currPos, 2) === peg$c7) { - s1 = peg$c7; - peg$currPos += 2; + 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) { - s3 = peg$parseInsert(); - if (s3 !== peg$FAILED) { - s4 = peg$parseInsert(); - if (s4 === peg$FAILED) { - s4 = null; - } - rule$expects(peg$e10); - if (input.charCodeAt(peg$currPos) === 41) { - s5 = peg$c8; + 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 { - s5 = peg$FAILED; + s3 = peg$FAILED; } - if (s5 !== peg$FAILED) { - peg$savedPos = s0; - s0 = peg$f15(s2, s3, s4); + if (s3 !== peg$FAILED) { + s0 = s2; } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1082,43 +1113,67 @@ function peg$parse(input, options) { peg$currPos = s0; s0 = peg$FAILED; } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - return s0; - } - - function peg$parseInsert() { - 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$f16(); - 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) { - s0 = s3; + 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; @@ -1127,94 +1182,160 @@ function peg$parse(input, options) { peg$currPos = s0; s0 = peg$FAILED; } - } else { - peg$currPos = s0; - s0 = peg$FAILED; } - } else { - peg$currPos = s0; - s0 = peg$FAILED; } return s0; } - function peg$parseCaseFold() { - var s0, s1; + 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$e11); - if (input.substr(peg$currPos, 2) === peg$c9) { - s1 = peg$c9; - peg$currPos += 2; + 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$f17(); + s1 = peg$f13(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - rule$expects(peg$e12); - if (input.substr(peg$currPos, 2) === peg$c10) { - s1 = peg$c10; - peg$currPos += 2; + 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$f18(); + s1 = peg$f14(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - rule$expects(peg$e13); - if (input.substr(peg$currPos, 2) === peg$c11) { - s1 = peg$c11; - peg$currPos += 2; + 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$f19(); + s1 = peg$f15(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - rule$expects(peg$e14); - if (input.substr(peg$currPos, 2) === peg$c12) { - s1 = peg$c12; - peg$currPos += 2; + rule$expects(peg$e12); + if (input.charCodeAt(peg$currPos) === 43) { + s1 = peg$c10; + peg$currPos++; } else { s1 = peg$FAILED; } if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$f20(); + 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; } - s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - rule$expects(peg$e15); - if (input.substr(peg$currPos, 2) === peg$c13) { - s1 = peg$c13; - peg$currPos += 2; + rule$expects(peg$e13); + if (input.charCodeAt(peg$currPos) === 45) { + s1 = peg$c11; + peg$currPos++; } else { s1 = peg$FAILED; } - if (s1 !== peg$FAILED) { + if (s1 === peg$FAILED) { + s1 = null; + } + s2 = peg$parseString(); + if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f21(); + 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; + } } - s0 = s1; } } } @@ -1233,7 +1354,7 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = peg$currPos; s2 = []; - rule$expects(peg$e16); + rule$expects(peg$e15); if (peg$r1.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; @@ -1243,7 +1364,7 @@ function peg$parse(input, options) { if (s3 !== peg$FAILED) { while (s3 !== peg$FAILED) { s2.push(s3); - rule$expects(peg$e16); + rule$expects(peg$e15); if (peg$r1.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; @@ -1261,7 +1382,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f22(s1); + s1 = peg$f19(s1); } s0 = s1; @@ -1296,7 +1417,7 @@ function peg$parse(input, options) { } if (s3 !== peg$FAILED) { peg$savedPos = peg$currPos; - s4 = peg$f23(s3); + s4 = peg$f20(s3); if (s4) { s4 = undefined; } else { @@ -1335,7 +1456,7 @@ function peg$parse(input, options) { } if (s3 !== peg$FAILED) { peg$savedPos = peg$currPos; - s4 = peg$f23(s3); + s4 = peg$f20(s3); if (s4) { s4 = undefined; } else { @@ -1358,7 +1479,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f24(s1); + s1 = peg$f21(s1); } s0 = s1; @@ -1373,7 +1494,7 @@ function peg$parse(input, options) { } peg$savedPos = peg$currPos; - s0 = peg$f25(); + s0 = peg$f22(); if (s0) { s0 = undefined; } else { @@ -1391,219 +1512,26 @@ function peg$parse(input, options) { } s0 = peg$currPos; - rule$expects(peg$e17); - if (input.substr(peg$currPos, 2) === peg$c14) { - s1 = peg$c14; - peg$currPos += 2; - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$f26(); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - rule$expects(peg$e18); - if (input.substr(peg$currPos, 2) === peg$c15) { - s1 = peg$c15; - peg$currPos += 2; - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$f27(); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - rule$expects(peg$e19); - if (input.substr(peg$currPos, 2) === peg$c16) { - s1 = peg$c16; - peg$currPos += 2; - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$f28(); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - rule$expects(peg$e20); - if (input.substr(peg$currPos, 2) === peg$c17) { - s1 = peg$c17; - peg$currPos += 2; - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$f29(); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - rule$expects(peg$e21); - if (input.substr(peg$currPos, 2) === peg$c18) { - s1 = peg$c18; - peg$currPos += 2; - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$f30(); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - rule$expects(peg$e22); - if (input.substr(peg$currPos, 2) === peg$c19) { - s1 = peg$c19; - peg$currPos += 2; - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$f31(); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - rule$expects(peg$e12); - if (input.substr(peg$currPos, 2) === peg$c10) { - s1 = peg$c10; - peg$currPos += 2; - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - s2 = peg$parseUTF32(); - if (s2 === peg$FAILED) { - s2 = peg$parseUTF16(); - } - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s0 = peg$f32(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$e23); - if (input.charCodeAt(peg$currPos) === 92) { - s1 = peg$c20; - 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$f33(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; - } - - function peg$parseUTF32() { - var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; - - var rule$expects = function (expected) { - if (peg$silentFails === 0) peg$expect(expected); - } - - s0 = peg$currPos; - rule$expects(peg$e24); - if (input.charCodeAt(peg$currPos) === 123) { - s1 = peg$c21; + rule$expects(peg$e16); + if (input.charCodeAt(peg$currPos) === 92) { + s1 = peg$c13; peg$currPos++; } else { s1 = peg$FAILED; } if (s1 !== peg$FAILED) { - s2 = peg$currPos; - s3 = peg$currPos; - s4 = peg$parseHex(); - if (s4 !== peg$FAILED) { - s5 = peg$parseHex(); - if (s5 === peg$FAILED) { - s5 = null; - } - s6 = peg$parseHex(); - if (s6 === peg$FAILED) { - s6 = null; - } - s7 = peg$parseHex(); - if (s7 === peg$FAILED) { - s7 = null; - } - s8 = peg$parseHex(); - if (s8 === peg$FAILED) { - s8 = null; - } - s9 = peg$parseHex(); - if (s9 === peg$FAILED) { - s9 = null; - } - s4 = [s4, s5, s6, s7, s8, s9]; - s3 = s4; - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - if (s3 !== peg$FAILED) { - s2 = input.substring(s2, peg$currPos); + rule$expects(peg$e3); + if (input.length > peg$currPos) { + s2 = input.charAt(peg$currPos); + peg$currPos++; } else { - s2 = s3; + s2 = peg$FAILED; } if (s2 !== peg$FAILED) { - rule$expects(peg$e2); - if (input.charCodeAt(peg$currPos) === 125) { - s3 = peg$c2; - peg$currPos++; + peg$savedPos = peg$currPos; + s3 = peg$f23(s2); + if (s3) { + s3 = undefined; } else { s3 = peg$FAILED; } @@ -1625,82 +1553,16 @@ function peg$parse(input, options) { return s0; } - function peg$parseUTF16() { - var s0, s1, s2, s3, s4, s5; - - var rule$expects = function (expected) { - if (peg$silentFails === 0) peg$expect(expected); - } - - s0 = peg$currPos; - s1 = peg$currPos; - s2 = peg$parseHex(); - if (s2 !== peg$FAILED) { - s3 = peg$parseHex(); - if (s3 !== peg$FAILED) { - s4 = peg$parseHex(); - if (s4 !== peg$FAILED) { - s5 = peg$parseHex(); - if (s5 !== peg$FAILED) { - s2 = [s2, s3, s4, s5]; - s1 = s2; - } else { - peg$currPos = s1; - s1 = peg$FAILED; - } - } else { - peg$currPos = s1; - s1 = peg$FAILED; - } - } else { - peg$currPos = s1; - s1 = peg$FAILED; - } - } else { - peg$currPos = s1; - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - s0 = input.substring(s0, peg$currPos); - } else { - s0 = s1; - } - - return s0; - } - - function peg$parseHex() { - var s0; - - var rule$expects = function (expected) { - if (peg$silentFails === 0) peg$expect(expected); - } - - rule$expects(peg$e25); - if (peg$r2.test(input.charAt(peg$currPos))) { - s0 = input.charAt(peg$currPos); - peg$currPos++; - } else { - 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)) - const lowerFirst = string => string.replace(/^\p{CWU}/u, char => toLower(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) - // At some point phase out old textmate syntax - let legacySyntax = false - peg$begin(); peg$result = peg$startRuleFunction(); diff --git a/parser/snippet-body-parser.pegjs b/parser/snippet-body-parser.pegjs index 9d308274..c6a13d0a 100644 --- a/parser/snippet-body-parser.pegjs +++ b/parser/snippet-body-parser.pegjs @@ -2,70 +2,52 @@ const toUpper = string => string.toLocaleUpperCase() const toLower = string => string.toLocaleLowerCase() const upperFirst = string => string.replace(/^\p{CWU}/u, char => toUpper(char)) - const lowerFirst = string => string.replace(/^\p{CWU}/u, char => toLower(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) - - // At some point phase out old textmate syntax - let legacySyntax = false } -/* -Test recursion $1 $a ${2} ${b} ${c:d} ${e:f${g:h}i} ${j:k${l|m,n,o${p}q,r${s:t}u,v${w|y,x|}z|}} $å ${ä} ${ö|1,2,3|} -Test transforms ... -Test escape sequences ... -*/ - -Snippet = body:(Expression / String)+ { return new Snippet(body, legacySyntax) } +Snippet = body:(Expression / String)+ { return new Snippet(body) } Expression - = "$" @Construct - / "${" construct:Construct modifier:Modifier "}" { return modifier.modify(construct) } - -Construct - = identifier:Tabstop { return new Tabstop(identifier) } - / identifier:Variable { return new Variable(identifier) } + = "$" 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) })+ -Modifier - = choices:Choice { return new Choice(choices) } - / content:Placeholder { return new Placeholder(content) } - / transformation:Transformation { return new Transformation(transformation) } - / "" { return new Modifier() } - Choice = "|" first:Selection rest:("," @Selection)* "|" { return [...rest, first] } -Selection = & { return escape('$,|') } @Snippet EOL +Selection = & { return escape(',|') } @String EOL Placeholder = ":" & { /*{*/ return escape('$}') } @Snippet EOL Transformation = "/" @RegExp "/" @Format "/" @$[gimsuy]* -RegExp = & { return escapes.push('/') } @String EOL +RegExp = & { return escape('/') } @String EOL Format - = & { return escape('\\(/') } @(ConditionalInsert / CaseFold / String)+ EOL + = & { return escape('$/') } @(Insert / String)+ EOL / "" { return [''] } -ConditionalInsert - = "(?" group:Int insert:Insert replacement:Insert? ")" { - return { group: `$${group || '$'}`, insert, replacement } - } +Insert + = '$' @Int + / '${' @Int '}' + / '${' @Int ':' & { /*{*/ return escape('}') } @Transform EOL '}' -Insert = ":" & { return escape(':)') } @String EOL - -CaseFold - = "\\E" { return sequence => sequence } - / "\\u" { return upperFirst } - / "\\l" { return lowerFirst } - / "\\U" { return toUpper } - / "\\L" { return toLower } +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) } @@ -73,20 +55,4 @@ String = chars:(@Escape / @char:. & { return !escaped(char) })+ { return chars.j EOL = & { return escapes.pop() } -Escape - = "\\n" { return '\n' } - / "\\r" { return '\r' } - / "\\v" { return '\v' } - / "\\t" { return '\t' } - / "\\b" { return '\b' } - / "\\f" { return '\f' } - / "\\u" codepoint:(UTF32 / UTF16) { - return String.fromCodePoint(Number.parseInt(codepoint, 16)) - } - / "\\" @char:. & { return escaped(char) } - -UTF32 = "{" @$(Hex Hex? Hex? Hex? Hex? Hex?) "}" - -UTF16 = $(Hex Hex Hex Hex) - -Hex = [0-9a-f]i +Escape = "\\" @char:. & { return escaped(char) } diff --git a/snippets.js b/snippets.js index 22e49d03..baefc9ef 100644 --- a/snippets.js +++ b/snippets.js @@ -2,7 +2,8 @@ const { CompositeDisposable, File } = require('atom') const CSON = require('season') const path = require('path') -const fs = require('fs').promises + +const { promises: fs } = require('fs') const ScopedPropertyStore = require('scoped-property-store')