diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000000..19f26c029b4 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5822 @@ +{ + "name": "vim", + "version": "0.10.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/glob": { + "version": "5.0.33", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.33.tgz", + "integrity": "sha512-BcD4yyWz+qmCggaYMSFF0Xn7GkO6tgwm3Fh9Gxk/kQmEU3Z7flQTnVlMyKBUNvXXNTCCyjqK4XT4/2hLd1gQ2A==", + "dev": true, + "requires": { + "@types/minimatch": "3.0.1", + "@types/node": "6.0.89" + } + }, + "@types/minimatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.1.tgz", + "integrity": "sha512-rUO/jz10KRSyA9SHoCWQ8WX9BICyj5jZYu1/ucKEJKb4KzLZCKMURdYbadP157Q6Zl1x0vHsrU+Z/O0XlhYQDw==", + "dev": true + }, + "@types/mocha": { + "version": "2.2.43", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.43.tgz", + "integrity": "sha512-xNlAmH+lRJdUMXClMTI9Y0pRqIojdxfm7DHsIxoB2iTzu3fnPmSMEN8SsSx0cdwV36d02PWCWaDUoZPDSln+xw==", + "dev": true + }, + "@types/node": { + "version": "6.0.89", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.89.tgz", + "integrity": "sha512-Z/67L97+6H1qJiEEHSN1SQapkWjDss1D90rAnFcQ6UxKkah9juzotK5UNEP1bDv/0lJ3NAQTnVfc/JWdgCGruA==", + "dev": true + }, + "@types/shelljs": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.7.4.tgz", + "integrity": "sha512-/RQcPAeUMEz4h2p818OZ4ghBJEliVf2MneJGiKl35guqJ1+CTNu+v1zP1qG9Dym61aktec60riIkja1X+GPS3A==", + "dev": true, + "requires": { + "@types/glob": "5.0.33", + "@types/node": "6.0.89" + } + }, + "@types/which": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/@types/which/-/which-1.0.28.tgz", + "integrity": "sha1-AW44dim4gXvtZT/jLqtdESecjfY=", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "agent-base": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz", + "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=", + "dev": true, + "requires": { + "extend": "3.0.1", + "semver": "5.0.3" + }, + "dependencies": { + "semver": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz", + "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no=", + "dev": true + } + } + }, + "ajv": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz", + "integrity": "sha1-wG9Zh3jETGsWGrr+NGa4GtGBTtI=", + "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "json-schema-traverse": "0.3.1", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "dev": true + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "any-shell-escape": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/any-shell-escape/-/any-shell-escape-0.1.1.tgz", + "integrity": "sha1-1Vq5ciRMcaml4asIefML8RCAaVk=", + "dev": true + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "1.1.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "dev": true + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-slice": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.0.0.tgz", + "integrity": "sha1-5zA08A3MH0CHYAj9IP6ud71LfC8=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true + }, + "async": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", + "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", + "dev": true, + "requires": { + "lodash": "4.17.4" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", + "dev": true + }, + "bl": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz", + "integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=", + "requires": { + "readable-stream": "2.3.3" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" + } + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "bump-regex": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/bump-regex/-/bump-regex-2.8.0.tgz", + "integrity": "sha512-prjTDXzGEbTvCgDVEAKvOGpAqZnz5EmzJNiYi2L72TjNy+T91w3SbPgofnAsLXZZBqZigv+kN4oF5oEIyr6LPw==", + "dev": true, + "requires": { + "semver": "5.4.1", + "xtend": "4.0.1" + }, + "dependencies": { + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + } + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "2.1.1", + "map-obj": "1.0.1" + } + }, + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "clipboardy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.1.4.tgz", + "integrity": "sha1-UbF1dPxoJYji3Slc+m5qoQnqte4=", + "requires": { + "execa": "0.6.3" + } + }, + "clone": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "cloneable-readable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.0.0.tgz", + "integrity": "sha1-pikNQT8hemEjL5XkWP84QYz7ARc=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "process-nextick-args": "1.0.7", + "through2": "2.0.3" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "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=", + "dev": true + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "configstore": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz", + "integrity": "sha1-c3o6cDbpiGECqmCZ5HuzOrGroaE=", + "dev": true, + "requires": { + "dot-prop": "3.0.0", + "graceful-fs": "4.1.11", + "mkdirp": "0.5.1", + "object-assign": "4.1.1", + "os-tmpdir": "1.0.2", + "osenv": "0.1.4", + "uuid": "2.0.3", + "write-file-atomic": "1.3.4", + "xdg-basedir": "2.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "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 + } + } + }, + "convert-source-map": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "requires": { + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", + "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + } + } + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true, + "requires": { + "boom": "2.10.1" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "1.0.2" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "dateformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", + "dev": true + }, + "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" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "deep-assign": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/deep-assign/-/deep-assign-1.0.0.tgz", + "integrity": "sha1-sJJ0O+hCfcYh6gBnzex+cN0Z83s=", + "dev": true, + "requires": { + "is-obj": "1.0.1" + } + }, + "deep-extend": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", + "dev": true + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "1.0.2" + } + }, + "define-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "dev": true, + "requires": { + "foreach": "2.0.5", + "object-keys": "1.0.11" + }, + "dependencies": { + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", + "dev": true + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "deprecated": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz", + "integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=", + "dev": true + }, + "detect-file": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-0.1.0.tgz", + "integrity": "sha1-STXe39lIhkjgBrASlWbpOGcR6mM=", + "dev": true, + "requires": { + "fs-exists-sync": "0.1.0" + } + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "2.0.1" + } + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "dev": true + }, + "diff-match-patch": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.0.tgz", + "integrity": "sha1-HMPIOkkNZ/ldkeOfatHy4Ia2MEg=" + }, + "dot-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", + "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", + "dev": true, + "requires": { + "is-obj": "1.0.1" + } + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "dev": true, + "requires": { + "readable-stream": "1.1.14" + } + }, + "duplexify": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.1.tgz", + "integrity": "sha512-j5goxHTwVED1Fpe5hh3q9R93Kip0Bg2KVAt4f8CEYM3UEwYcPSvWbXaUQOzdX/HtiNomipv+gU7ASQPDbV7pGQ==", + "dev": true, + "requires": { + "end-of-stream": "1.4.0", + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "stream-shift": "1.0.0" + }, + "dependencies": { + "end-of-stream": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz", + "integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=", + "dev": true, + "requires": { + "once": "1.4.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "end-of-stream": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", + "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", + "dev": true, + "requires": { + "once": "1.3.3" + } + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, + "requires": { + "is-arrayish": "0.2.1" + } + }, + "es-abstract": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.9.0.tgz", + "integrity": "sha512-kk3IJoKo7A3pWJc0OV8yZ/VEX2oSUytfekrJiqoxBlKJMFAJVJVpGdHClCCTdv+Fn2zHfpDHHIelMFhZVfef3Q==", + "dev": true, + "requires": { + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.1", + "is-callable": "1.1.3", + "is-regex": "1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true, + "requires": { + "is-callable": "1.1.3", + "is-date-object": "1.0.1", + "is-symbol": "1.0.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 + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "dev": true, + "requires": { + "duplexer": "0.1.1", + "from": "0.1.7", + "map-stream": "0.1.0", + "pause-stream": "0.0.11", + "split": "0.3.3", + "stream-combiner": "0.0.4", + "through": "2.3.8" + } + }, + "execa": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.6.3.tgz", + "integrity": "sha1-V7aaWU8IF1nGnlNw8NF7nLEWWP4=", + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "0.1.1" + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "2.2.3" + } + }, + "expand-tilde": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", + "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", + "dev": true, + "requires": { + "os-homedir": "1.0.2" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "1.1.0" + }, + "dependencies": { + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + } + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fancy-log": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.0.tgz", + "integrity": "sha1-Rb4X0Cu5kX1gzP/UmVyZnmyMmUg=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "time-stamp": "1.1.0" + } + }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "dev": true + }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "dev": true, + "requires": { + "pend": "1.2.0" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fill-range": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "dev": true, + "requires": { + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" + } + }, + "find-index": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", + "integrity": "sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=", + "dev": true + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "findup-sync": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.4.3.tgz", + "integrity": "sha1-QAQ5Kee8YK3wt/SCfExudaDeyhI=", + "dev": true, + "requires": { + "detect-file": "0.1.0", + "is-glob": "2.0.1", + "micromatch": "2.3.11", + "resolve-dir": "0.1.1" + } + }, + "fined": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.0.tgz", + "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", + "dev": true, + "requires": { + "expand-tilde": "2.0.2", + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0", + "object.pick": "1.3.0", + "parse-filepath": "1.0.1" + }, + "dependencies": { + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "1.0.1" + } + } + } + }, + "first-chunk-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", + "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=", + "dev": true + }, + "flagged-respawn": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-0.3.2.tgz", + "integrity": "sha1-/xke3c1wiKZ1smEP/8l2vpuAdLU=", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "1.0.2" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", + "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "dev": true + }, + "fs-exists-sync": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", + "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gaze": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz", + "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", + "dev": true, + "requires": { + "globule": "0.1.0" + } + }, + "generate-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "dev": true + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "requires": { + "is-property": "1.0.2" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "glob": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", + "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "2.0.10", + "once": "1.3.3" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "2.0.0", + "is-glob": "2.0.1" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "2.0.1" + } + }, + "glob-stream": { + "version": "3.1.18", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", + "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", + "dev": true, + "requires": { + "glob": "4.5.3", + "glob2base": "0.0.12", + "minimatch": "2.0.10", + "ordered-read-streams": "0.1.0", + "through2": "0.6.5", + "unique-stream": "1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + } + } + }, + "glob-watcher": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", + "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", + "dev": true, + "requires": { + "gaze": "0.5.2" + } + }, + "glob2base": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", + "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", + "dev": true, + "requires": { + "find-index": "0.1.1" + } + }, + "global-modules": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", + "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", + "dev": true, + "requires": { + "global-prefix": "0.1.5", + "is-windows": "0.2.0" + } + }, + "global-prefix": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", + "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", + "dev": true, + "requires": { + "homedir-polyfill": "1.0.1", + "ini": "1.3.4", + "is-windows": "0.2.0", + "which": "1.3.0" + } + }, + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", + "dev": true, + "requires": { + "glob": "3.1.21", + "lodash": "1.0.2", + "minimatch": "0.2.14" + }, + "dependencies": { + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", + "dev": true, + "requires": { + "graceful-fs": "1.2.3", + "inherits": "1.0.2", + "minimatch": "0.2.14" + } + }, + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=", + "dev": true + }, + "inherits": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=", + "dev": true + }, + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", + "integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=", + "dev": true + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "dev": true, + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + } + } + }, + "glogg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.0.tgz", + "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=", + "dev": true, + "requires": { + "sparkles": "1.0.0" + } + }, + "graceful-fs": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", + "dev": true, + "requires": { + "natives": "1.1.0" + } + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "dev": true + }, + "gulp": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", + "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", + "dev": true, + "requires": { + "archy": "1.0.0", + "chalk": "1.1.3", + "deprecated": "0.0.1", + "gulp-util": "3.0.8", + "interpret": "1.0.4", + "liftoff": "2.3.0", + "minimist": "1.2.0", + "orchestrator": "0.3.8", + "pretty-hrtime": "1.0.3", + "semver": "4.3.6", + "tildify": "1.2.0", + "v8flags": "2.1.1", + "vinyl-fs": "0.3.14" + } + }, + "gulp-bump": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/gulp-bump/-/gulp-bump-2.8.0.tgz", + "integrity": "sha512-syvQLax2xQo1EDFJxanUqX1rv+YkVB4/cx/THN+uInmSjMGezT1/6WYLdXqkBAMQUw2KlyB2melz0DzrHdwkLA==", + "dev": true, + "requires": { + "bump-regex": "2.8.0", + "plugin-error": "0.1.2", + "plugin-log": "0.1.0", + "semver": "5.4.1", + "through2": "2.0.3" + }, + "dependencies": { + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + } + } + }, + "gulp-chmod": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-chmod/-/gulp-chmod-2.0.0.tgz", + "integrity": "sha1-AMOQuSigeZslGsz2MaoJ4BzGKZw=", + "dev": true, + "requires": { + "deep-assign": "1.0.0", + "stat-mode": "0.2.2", + "through2": "2.0.3" + } + }, + "gulp-filter": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/gulp-filter/-/gulp-filter-5.0.1.tgz", + "integrity": "sha512-5olRzAhFdXB2klCu1lnazP65aO9YdA/5WfC9VdInIc8PrUeDIoZfaA3Edb0yUBGhVdHv4eHKL9Fg5tUoEJ9z5A==", + "dev": true, + "requires": { + "gulp-util": "3.0.8", + "multimatch": "2.1.0", + "streamfilter": "1.0.5" + } + }, + "gulp-git": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/gulp-git/-/gulp-git-2.4.2.tgz", + "integrity": "sha512-YgFW2PmtGSHkUYFql75ByRqFjiEDvOk7jxVkkqH0OSDKc3icxD8GDBR1kbr2zG7oNaIynk2NcKVSypgPHl7ibQ==", + "dev": true, + "requires": { + "any-shell-escape": "0.1.1", + "gulp-util": "3.0.8", + "require-dir": "0.3.2", + "strip-bom-stream": "3.0.0", + "through2": "2.0.3", + "vinyl": "2.1.0" + }, + "dependencies": { + "clone": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", + "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "vinyl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", + "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", + "dev": true, + "requires": { + "clone": "2.1.1", + "clone-buffer": "1.0.0", + "clone-stats": "1.0.0", + "cloneable-readable": "1.0.0", + "remove-trailing-separator": "1.1.0", + "replace-ext": "1.0.0" + } + } + } + }, + "gulp-gunzip": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/gulp-gunzip/-/gulp-gunzip-0.0.3.tgz", + "integrity": "sha1-e24HsPWP09QlFcSOrVpj3wVy9i8=", + "dev": true, + "requires": { + "through2": "0.6.5", + "vinyl": "0.4.6" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "dev": true, + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + } + } + }, + "gulp-inject-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gulp-inject-string/-/gulp-inject-string-1.1.0.tgz", + "integrity": "sha1-8C3e2w91GN+61Uj30t8FuuLtH2Q=", + "dev": true, + "requires": { + "event-stream": "3.3.4", + "gulp-util": "3.0.8" + } + }, + "gulp-remote-src": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/gulp-remote-src/-/gulp-remote-src-0.4.3.tgz", + "integrity": "sha1-VyjP1kNDPdSEXd7wlp8PlxoqtKE=", + "dev": true, + "requires": { + "event-stream": "3.3.4", + "node.extend": "1.1.6", + "request": "2.79.0", + "through2": "2.0.3", + "vinyl": "2.0.2" + }, + "dependencies": { + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "request": { + "version": "2.79.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", + "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", + "dev": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.11.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "2.0.6", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "qs": "6.3.2", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.4.3", + "uuid": "3.1.0" + } + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", + "dev": true + }, + "vinyl": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.0.2.tgz", + "integrity": "sha1-CjcT2NTpIhxY8QyhbAEWyeJe2nw=", + "dev": true, + "requires": { + "clone": "1.0.2", + "clone-buffer": "1.0.0", + "clone-stats": "1.0.0", + "cloneable-readable": "1.0.0", + "is-stream": "1.1.0", + "remove-trailing-separator": "1.1.0", + "replace-ext": "1.0.0" + } + } + } + }, + "gulp-shell": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.6.3.tgz", + "integrity": "sha1-Lqpu3/+ovf96jwufmFKHbzMzHGs=", + "dev": true, + "requires": { + "async": "2.5.0", + "gulp-util": "3.0.8", + "lodash": "4.17.4", + "through2": "2.0.3" + } + }, + "gulp-sourcemaps": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz", + "integrity": "sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw=", + "dev": true, + "requires": { + "convert-source-map": "1.5.0", + "graceful-fs": "4.1.11", + "strip-bom": "2.0.0", + "through2": "2.0.3", + "vinyl": "1.2.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "1.0.2", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "gulp-symdest": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gulp-symdest/-/gulp-symdest-1.1.0.tgz", + "integrity": "sha1-wWUyBzLRks5W/ZQnH/oSMjS/KuA=", + "dev": true, + "requires": { + "event-stream": "3.3.4", + "mkdirp": "0.5.1", + "queue": "3.1.0", + "vinyl-fs": "2.4.4" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "2.0.10", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + } + }, + "glob-stream": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", + "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", + "dev": true, + "requires": { + "extend": "3.0.1", + "glob": "5.0.15", + "glob-parent": "3.1.0", + "micromatch": "2.3.11", + "ordered-read-streams": "0.3.0", + "through2": "0.6.5", + "to-absolute-glob": "0.1.1", + "unique-stream": "2.2.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + } + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "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-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "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 + }, + "ordered-read-streams": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", + "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", + "dev": true, + "requires": { + "is-stream": "1.1.0", + "readable-stream": "2.3.3" + } + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-bom-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", + "dev": true, + "requires": { + "first-chunk-stream": "1.0.0", + "strip-bom": "2.0.0" + } + }, + "unique-stream": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", + "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", + "dev": true, + "requires": { + "json-stable-stringify": "1.0.1", + "through2-filter": "2.0.0" + } + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "1.0.2", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + }, + "vinyl-fs": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", + "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", + "dev": true, + "requires": { + "duplexify": "3.5.1", + "glob-stream": "5.3.5", + "graceful-fs": "4.1.11", + "gulp-sourcemaps": "1.6.0", + "is-valid-glob": "0.3.0", + "lazystream": "1.0.0", + "lodash.isequal": "4.5.0", + "merge-stream": "1.0.1", + "mkdirp": "0.5.1", + "object-assign": "4.1.1", + "readable-stream": "2.3.3", + "strip-bom": "2.0.0", + "strip-bom-stream": "1.0.0", + "through2": "2.0.3", + "through2-filter": "2.0.0", + "vali-date": "1.0.0", + "vinyl": "1.2.0" + } + } + } + }, + "gulp-tag-version": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/gulp-tag-version/-/gulp-tag-version-1.3.0.tgz", + "integrity": "sha1-hEjIfu0YZtuObLWYvEGb4t98R9s=", + "dev": true, + "requires": { + "gulp-git": "0.3.6", + "gulp-util": "2.2.20", + "map-stream": "0.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", + "dev": true + }, + "ansi-styles": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", + "dev": true + }, + "chalk": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "dev": true, + "requires": { + "ansi-styles": "1.1.0", + "escape-string-regexp": "1.0.5", + "has-ansi": "0.1.0", + "strip-ansi": "0.3.0", + "supports-color": "0.2.0" + } + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "4.0.1", + "meow": "3.7.0" + } + }, + "gulp-git": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/gulp-git/-/gulp-git-0.3.6.tgz", + "integrity": "sha1-d+w9oiklwkbt15bKENQWOWXYFAo=", + "dev": true, + "requires": { + "any-shell-escape": "0.1.1", + "gulp-util": "2.2.20", + "map-stream": "0.1.0", + "through2": "0.4.2" + } + }, + "gulp-util": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz", + "integrity": "sha1-1xRuVyiRC9jwR6awseVJvCLb1kw=", + "dev": true, + "requires": { + "chalk": "0.5.1", + "dateformat": "1.0.12", + "lodash._reinterpolate": "2.4.1", + "lodash.template": "2.4.1", + "minimist": "0.2.0", + "multipipe": "0.1.2", + "through2": "0.5.1", + "vinyl": "0.2.3" + }, + "dependencies": { + "through2": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "3.0.0" + } + } + } + }, + "has-ansi": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "dev": true, + "requires": { + "ansi-regex": "0.2.1" + } + }, + "lodash._reinterpolate": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz", + "integrity": "sha1-TxInqlqHEfxjL1sHofRgequLMiI=", + "dev": true + }, + "lodash.escape": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz", + "integrity": "sha1-LOEsXghNsKV92l5dHu659dF1o7Q=", + "dev": true, + "requires": { + "lodash._escapehtmlchar": "2.4.1", + "lodash._reunescapedhtml": "2.4.1", + "lodash.keys": "2.4.1" + } + }, + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" + } + }, + "lodash.template": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz", + "integrity": "sha1-nmEQB+32KRKal0qzxIuBez4c8g0=", + "dev": true, + "requires": { + "lodash._escapestringchar": "2.4.1", + "lodash._reinterpolate": "2.4.1", + "lodash.defaults": "2.4.1", + "lodash.escape": "2.4.1", + "lodash.keys": "2.4.1", + "lodash.templatesettings": "2.4.1", + "lodash.values": "2.4.1" + } + }, + "lodash.templatesettings": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz", + "integrity": "sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk=", + "dev": true, + "requires": { + "lodash._reinterpolate": "2.4.1", + "lodash.escape": "2.4.1" + } + }, + "minimist": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", + "integrity": "sha1-Tf/lJdriuGTGbC4jxicdev3s784=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "strip-ansi": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "dev": true, + "requires": { + "ansi-regex": "0.2.1" + } + }, + "supports-color": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", + "dev": true + }, + "through2": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", + "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "2.1.2" + }, + "dependencies": { + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "dev": true, + "requires": { + "object-keys": "0.4.0" + } + } + } + }, + "vinyl": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.2.3.tgz", + "integrity": "sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI=", + "dev": true, + "requires": { + "clone-stats": "0.0.1" + } + }, + "xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", + "dev": true + } + } + }, + "gulp-tslint": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gulp-tslint/-/gulp-tslint-8.1.2.tgz", + "integrity": "sha512-0RNGqbp2TKPdbG+sWU3mNMXEMuF/noY1KS4+jd5lOStkvuFINkFL29dHX3IT1u+vVFD4Glwf+lkcdR2QMVNMzA==", + "dev": true, + "requires": { + "gulp-util": "3.0.8", + "map-stream": "0.0.7", + "through": "2.3.8" + }, + "dependencies": { + "map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", + "dev": true + } + } + }, + "gulp-typings": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/gulp-typings/-/gulp-typings-2.0.4.tgz", + "integrity": "sha1-z7/yMGfR8sRlWqKM87g/QJMQFY8=", + "dev": true, + "requires": { + "through2": "2.0.3", + "typings-core": "1.6.1", + "typings-global": "1.0.20" + } + }, + "gulp-untar": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/gulp-untar/-/gulp-untar-0.0.6.tgz", + "integrity": "sha1-1r3v3n6ajgVMnxYjhaB4LEvnQAA=", + "dev": true, + "requires": { + "event-stream": "3.3.4", + "gulp-util": "3.0.8", + "streamifier": "0.1.1", + "tar": "2.2.1", + "through2": "2.0.3" + } + }, + "gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", + "dev": true, + "requires": { + "array-differ": "1.0.0", + "array-uniq": "1.0.3", + "beeper": "1.1.1", + "chalk": "1.1.3", + "dateformat": "2.2.0", + "fancy-log": "1.3.0", + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "lodash._reescape": "3.0.0", + "lodash._reevaluate": "3.0.0", + "lodash._reinterpolate": "3.0.0", + "lodash.template": "3.6.2", + "minimist": "1.2.0", + "multipipe": "0.1.2", + "object-assign": "3.0.0", + "replace-ext": "0.0.1", + "through2": "2.0.3", + "vinyl": "0.5.3" + } + }, + "gulp-vinyl-zip": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/gulp-vinyl-zip/-/gulp-vinyl-zip-1.4.0.tgz", + "integrity": "sha1-VjgvLMtXIxuwR4x4c3zNVylzvuE=", + "dev": true, + "requires": { + "event-stream": "3.3.4", + "queue": "3.1.0", + "through2": "0.6.5", + "vinyl": "0.4.6", + "vinyl-fs": "2.4.4", + "yauzl": "2.8.0", + "yazl": "2.4.2" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "dev": true + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "2.0.10", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + } + }, + "glob-stream": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", + "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", + "dev": true, + "requires": { + "extend": "3.0.1", + "glob": "5.0.15", + "glob-parent": "3.1.0", + "micromatch": "2.3.11", + "ordered-read-streams": "0.3.0", + "through2": "0.6.5", + "to-absolute-glob": "0.1.1", + "unique-stream": "2.2.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "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-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, + "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 + }, + "ordered-read-streams": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", + "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", + "dev": true, + "requires": { + "is-stream": "1.1.0", + "readable-stream": "2.3.3" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-bom-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", + "dev": true, + "requires": { + "first-chunk-stream": "1.0.0", + "strip-bom": "2.0.0" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + }, + "unique-stream": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", + "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", + "dev": true, + "requires": { + "json-stable-stringify": "1.0.1", + "through2-filter": "2.0.0" + } + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "dev": true, + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + }, + "vinyl-fs": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", + "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", + "dev": true, + "requires": { + "duplexify": "3.5.1", + "glob-stream": "5.3.5", + "graceful-fs": "4.1.11", + "gulp-sourcemaps": "1.6.0", + "is-valid-glob": "0.3.0", + "lazystream": "1.0.0", + "lodash.isequal": "4.5.0", + "merge-stream": "1.0.1", + "mkdirp": "0.5.1", + "object-assign": "4.1.1", + "readable-stream": "2.3.3", + "strip-bom": "2.0.0", + "strip-bom-stream": "1.0.0", + "through2": "2.0.3", + "through2-filter": "2.0.0", + "vali-date": "1.0.0", + "vinyl": "1.2.0" + }, + "dependencies": { + "clone": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "2.3.3", + "xtend": "4.0.1" + } + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "1.0.2", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + } + } + } + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "dev": true, + "requires": { + "glogg": "1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "commander": "2.11.0", + "is-my-json-valid": "2.16.1", + "pinkie-promise": "2.0.1" + } + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, + "requires": { + "function-bind": "1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "dev": true, + "requires": { + "sparkles": "1.0.0" + } + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + }, + "homedir-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", + "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "dev": true, + "requires": { + "parse-passwd": "1.0.0" + } + }, + "hosted-git-info": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", + "dev": true + }, + "http-proxy-agent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz", + "integrity": "sha1-zBzjjkU7+YSg93AtLdWcc9CBKEo=", + "dev": true, + "requires": { + "agent-base": "2.1.1", + "debug": "2.6.9", + "extend": "3.0.1" + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "https-proxy-agent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz", + "integrity": "sha1-NffabEjOTdv6JkiRrFk+5f+GceY=", + "dev": true, + "requires": { + "agent-base": "2.1.1", + "debug": "2.6.9", + "extend": "3.0.1" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "2.0.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.3.3", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", + "dev": true + }, + "interpret": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.4.tgz", + "integrity": "sha1-ggzdWIuGj/sZGoCVBtbJyPISsbA=", + "dev": true + }, + "invariant": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", + "dev": true, + "requires": { + "loose-envify": "1.3.1" + } + }, + "is": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is/-/is-3.2.1.tgz", + "integrity": "sha1-0Kwq1V63sL7JJqUmb2xmKqqD3KU=", + "dev": true + }, + "is-absolute": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", + "integrity": "sha1-IN5p89uULvLYe5wto28XIjWxtes=", + "dev": true, + "requires": { + "is-relative": "0.2.1", + "is-windows": "0.2.0" + } + }, + "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-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-callable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, + "is-my-json-valid": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz", + "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", + "dev": true, + "requires": { + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "1.0.1" + } + }, + "is-relative": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", + "integrity": "sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU=", + "dev": true, + "requires": { + "is-unc-path": "0.1.2" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-unc-path": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", + "integrity": "sha1-arBTpyVzwQJQ/0FqOBTDUXivObk=", + "dev": true, + "requires": { + "unc-path-regex": "0.1.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-valid-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", + "integrity": "sha1-1LVcafUYhvm2XHDWwmItN+KfSP4=", + "dev": true + }, + "is-windows": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", + "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.5" + } + }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "dev": true, + "requires": { + "readable-stream": "2.3.3" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "liftoff": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.3.0.tgz", + "integrity": "sha1-qY8v9nGD2Lp8+soQVIvX/wVQs4U=", + "dev": true, + "requires": { + "extend": "3.0.1", + "findup-sync": "0.4.3", + "fined": "1.1.0", + "flagged-respawn": "0.3.2", + "lodash.isplainobject": "4.0.6", + "lodash.isstring": "4.0.1", + "lodash.mapvalues": "4.6.0", + "rechoir": "0.6.2", + "resolve": "1.4.0" + } + }, + "listify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/listify/-/listify-1.0.0.tgz", + "integrity": "sha1-A8p7otFQ1CZ3c/dOV1WNEFPSvuM=", + "dev": true + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + } + } + }, + "lockfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.3.tgz", + "integrity": "sha1-Jjj8OaAzHpysGgS3F5mTHJxQ33k=", + "dev": true + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + }, + "lodash._baseassign": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", + "dev": true, + "requires": { + "lodash._basecopy": "3.0.1", + "lodash.keys": "3.1.2" + } + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basecreate": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", + "dev": true + }, + "lodash._basetostring": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", + "dev": true + }, + "lodash._basevalues": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", + "dev": true + }, + "lodash._escapehtmlchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz", + "integrity": "sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0=", + "dev": true, + "requires": { + "lodash._htmlescapes": "2.4.1" + } + }, + "lodash._escapestringchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz", + "integrity": "sha1-7P4iYYoq3lC/7qQ5N+Ud9m8O23I=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._htmlescapes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz", + "integrity": "sha1-MtFL8IRLbeb4tioFG09nwii2JMs=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash._isnative": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz", + "integrity": "sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw=", + "dev": true + }, + "lodash._objecttypes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz", + "integrity": "sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE=", + "dev": true + }, + "lodash._reescape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", + "dev": true + }, + "lodash._reevaluate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash._reunescapedhtml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz", + "integrity": "sha1-dHxPxAED6zu4oJduVx96JlnpO6c=", + "dev": true, + "requires": { + "lodash._htmlescapes": "2.4.1", + "lodash.keys": "2.4.1" + }, + "dependencies": { + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" + } + } + } + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "dev": true + }, + "lodash._shimkeys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", + "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", + "dev": true, + "requires": { + "lodash._objecttypes": "2.4.1" + } + }, + "lodash.create": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "dev": true, + "requires": { + "lodash._baseassign": "3.2.0", + "lodash._basecreate": "3.0.3", + "lodash._isiterateecall": "3.0.9" + } + }, + "lodash.defaults": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", + "integrity": "sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ=", + "dev": true, + "requires": { + "lodash._objecttypes": "2.4.1", + "lodash.keys": "2.4.1" + }, + "dependencies": { + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" + } + } + } + }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "dev": true, + "requires": { + "lodash._root": "3.0.1" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true + }, + "lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "dev": true, + "requires": { + "lodash._objecttypes": "2.4.1" + } + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "dev": true + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" + } + }, + "lodash.mapvalues": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=", + "dev": true + }, + "lodash.restparam": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", + "dev": true + }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "dev": true, + "requires": { + "lodash._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" + } + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "dev": true, + "requires": { + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" + } + }, + "lodash.values": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz", + "integrity": "sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ=", + "dev": true, + "requires": { + "lodash.keys": "2.4.1" + }, + "dependencies": { + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" + } + } + } + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "dev": true, + "requires": { + "js-tokens": "3.0.2" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.2" + } + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", + "dev": true + }, + "make-error": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz", + "integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y=", + "dev": true + }, + "make-error-cause": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/make-error-cause/-/make-error-cause-1.2.2.tgz", + "integrity": "sha1-3wOI/NCzeBbf8KX7gQiTl3fcvJ0=", + "dev": true, + "requires": { + "make-error": "1.3.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.0", + "normalize-package-data": "2.4.0", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" + }, + "dependencies": { + "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 + } + } + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "dev": true, + "requires": { + "readable-stream": "2.3.3" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" + } + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", + "dev": true + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "dev": true, + "requires": { + "mime-db": "1.30.0" + } + }, + "minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "mocha": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.0.1.tgz", + "integrity": "sha512-evDmhkoA+cBNiQQQdSKZa2b9+W2mpLoj50367lhy+Klnx9OV8XlCIhigUnn1gaTFLQCa0kdNhEGDr0hCXOQFDw==", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.3.1", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "msgpack5": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-3.5.1.tgz", + "integrity": "sha512-Umzq9b2rUCSQMArFlBYdmYm0DvDCpcJWUNefG6sVd1VtAxngpueQMhsuHno6p9IKUT2tCKc7mS1dvpPb476R1A==", + "requires": { + "bl": "1.2.1", + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "safe-buffer": "5.1.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "msgpack5rpc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/msgpack5rpc/-/msgpack5rpc-1.1.0.tgz", + "integrity": "sha1-9Leqf4sgsxez2PbYuLLCQ29xbpA=", + "requires": { + "msgpack5": "3.5.1" + } + }, + "multimatch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", + "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", + "dev": true, + "requires": { + "array-differ": "1.0.0", + "array-union": "1.0.2", + "arrify": "1.0.1", + "minimatch": "3.0.4" + }, + "dependencies": { + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + } + } + }, + "multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "dev": true, + "requires": { + "duplexer2": "0.0.2" + } + }, + "natives": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.0.tgz", + "integrity": "sha1-6f+EFBimsux6SV6TmYT3jxY+bjE=", + "dev": true + }, + "neovim-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/neovim-client/-/neovim-client-2.1.0.tgz", + "integrity": "sha1-Wd+OnVljYlpPI4TUaqE6Zh1C8Rw=", + "requires": { + "lodash": "4.17.4", + "msgpack5rpc": "1.1.0", + "traverse": "0.6.6" + } + }, + "node.extend": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-1.1.6.tgz", + "integrity": "sha1-p7iCyC1sk6SGOlUEvV3o7IYli5Y=", + "dev": true, + "requires": { + "is": "3.2.1" + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "requires": { + "abbrev": "1.1.1" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "2.5.0", + "is-builtin-module": "1.0.0", + "semver": "4.3.6", + "validate-npm-package-license": "3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "1.1.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "2.0.1" + } + }, + "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=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", + "dev": true + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "dev": true, + "requires": { + "array-each": "1.0.1", + "array-slice": "1.0.0", + "for-own": "1.0.0", + "isobject": "3.0.1" + }, + "dependencies": { + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "1.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "1.1.2", + "es-abstract": "1.9.0" + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "0.1.5", + "is-extendable": "0.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "orchestrator": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz", + "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", + "dev": true, + "requires": { + "end-of-stream": "0.1.5", + "sequencify": "0.0.7", + "stream-consume": "0.1.0" + } + }, + "ordered-read-streams": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz", + "integrity": "sha1-/VZamvjrRHO6abbtijQ1LLVS8SY=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "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 + }, + "osenv": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "dev": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "parse-filepath": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.1.tgz", + "integrity": "sha1-FZ1hVdQ5BNFsEO9piRHaHpGWm3M=", + "dev": true, + "requires": { + "is-absolute": "0.2.6", + "map-cache": "0.2.2", + "path-root": "0.1.1" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" + } + }, + "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.3.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "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=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "requires": { + "path-root-regex": "0.1.2" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + } + } + }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "dev": true, + "requires": { + "through": "2.3.8" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "0.1.1", + "ansi-red": "0.1.1", + "arr-diff": "1.1.0", + "arr-union": "2.1.0", + "extend-shallow": "1.1.4" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "1.1.0", + "array-slice": "0.2.3" + } + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + } + } + }, + "plugin-log": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/plugin-log/-/plugin-log-0.1.0.tgz", + "integrity": "sha1-hgSc9qsQgzOYqTHzaJy67nteEzM=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "dateformat": "1.0.12" + }, + "dependencies": { + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "4.0.1", + "meow": "3.7.0" + } + } + } + }, + "popsicle": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/popsicle/-/popsicle-8.2.0.tgz", + "integrity": "sha1-/0QBAFyrQ6lBipFBBhHAAZdxLSE=", + "dev": true, + "requires": { + "any-promise": "1.3.0", + "arrify": "1.0.1", + "concat-stream": "1.6.0", + "form-data": "2.3.1", + "make-error-cause": "1.2.2", + "throwback": "1.1.1", + "tough-cookie": "2.3.3", + "xtend": "4.0.1" + } + }, + "popsicle-proxy-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/popsicle-proxy-agent/-/popsicle-proxy-agent-3.0.0.tgz", + "integrity": "sha1-uRM8VdlFdZq37mG3cRNkYg066tw=", + "dev": true, + "requires": { + "http-proxy-agent": "1.0.0", + "https-proxy-agent": "1.0.0" + } + }, + "popsicle-retry": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/popsicle-retry/-/popsicle-retry-3.2.1.tgz", + "integrity": "sha1-4G6GZTO0KnoSPrMwy+Y6fOvLoQw=", + "dev": true, + "requires": { + "any-promise": "1.3.0", + "xtend": "4.0.1" + } + }, + "popsicle-rewrite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/popsicle-rewrite/-/popsicle-rewrite-1.0.0.tgz", + "integrity": "sha1-HdTo6pwxgjUfuCD4eTTZkvf7kAc=", + "dev": true + }, + "popsicle-status": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/popsicle-status/-/popsicle-status-2.0.1.tgz", + "integrity": "sha1-jdcMT+fGlBCa3XhP/oDqysHnso0=", + "dev": true + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "prettier": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.7.4.tgz", + "integrity": "sha1-XoYkrpNjyA+V7GRFhOzfVddPk/o=" + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "promise-finally": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/promise-finally/-/promise-finally-2.2.1.tgz", + "integrity": "sha1-ImFsS6kCkW6Yi9RsVNfKoIkQzXc=", + "dev": true, + "requires": { + "any-promise": "1.3.0" + } + }, + "promised-neovim-client": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/promised-neovim-client/-/promised-neovim-client-2.0.2.tgz", + "integrity": "sha1-wDtO8hBJ63o/88AWlzG32dkjiSE=", + "requires": { + "lodash": "3.10.1", + "msgpack5rpc": "1.1.0", + "traverse": "0.6.6" + }, + "dependencies": { + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + } + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "qs": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", + "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", + "dev": true + }, + "querystringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", + "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=", + "dev": true + }, + "queue": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/queue/-/queue-3.1.0.tgz", + "integrity": "sha1-bEnQHwCeIlZ4h4nyv/rGuLmZBYU=", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "randomatic": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "rc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", + "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", + "dev": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "1.4.0" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "2.1.0", + "strip-indent": "1.0.1" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "0.1.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "1.0.2" + } + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "request": { + "version": "2.83.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", + "dev": true, + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.1", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "boom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "dev": true, + "requires": { + "hoek": "4.2.0" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "dev": true, + "requires": { + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "dev": true, + "requires": { + "hoek": "4.2.0" + } + } + } + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "dev": true, + "requires": { + "ajv": "5.2.3", + "har-schema": "2.0.0" + } + }, + "hawk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "dev": true, + "requires": { + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.0", + "sntp": "2.0.2" + } + }, + "hoek": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", + "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "dev": true + }, + "sntp": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz", + "integrity": "sha1-UGQRDwr4X3z9t9a2ekACjOUrSys=", + "dev": true, + "requires": { + "hoek": "4.2.0" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", + "dev": true + } + } + }, + "require-dir": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/require-dir/-/require-dir-0.3.2.tgz", + "integrity": "sha1-wdXHXp+//eny5rM+OD209ZS1pqk=", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", + "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==", + "dev": true, + "requires": { + "path-parse": "1.0.5" + } + }, + "resolve-dir": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", + "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", + "dev": true, + "requires": { + "expand-tilde": "1.2.2", + "global-modules": "0.2.3" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + } + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", + "dev": true + }, + "sequencify": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz", + "integrity": "sha1-kM/xnQLgcCf9dn9erT57ldHnOAw=", + "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=", + "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=" + }, + "shelljs": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", + "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", + "dev": true, + "requires": { + "glob": "7.1.2", + "interpret": "1.0.4", + "rechoir": "0.6.2" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + } + } + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "dev": true + }, + "smartq": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/smartq/-/smartq-1.1.6.tgz", + "integrity": "sha512-W7vTj4kSqN8kHVq+Q6BJTr/UGc36TnO0pzKNU8B4cr7TXG4N98eyubWaaCHPSjCUqDgmUPPru929WXzetai97A==", + "dev": true, + "requires": { + "typings-global": "1.0.20", + "util.promisify": "1.0.0" + } + }, + "smartshell": { + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/smartshell/-/smartshell-1.0.18.tgz", + "integrity": "sha512-yUeTSEUYbbxTPI1AI/vyYEnjRWW77dzKcQviGPyEn1Bu/cy7QFRtFdEVf7qhRFO8r1aqCS/MqT+obtgHphhlrg==", + "dev": true, + "requires": { + "@types/shelljs": "0.7.4", + "@types/which": "1.0.28", + "shelljs": "0.7.8", + "smartq": "1.1.6", + "typings-global": "1.0.20", + "which": "1.3.0" + } + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "1.1.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "0.5.7" + } + }, + "sparkles": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.0.tgz", + "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=", + "dev": true + }, + "spdx-correct": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "dev": true, + "requires": { + "spdx-license-ids": "1.2.2" + } + }, + "spdx-expression-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", + "dev": true + }, + "spdx-license-ids": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", + "dev": true + }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "dev": true, + "requires": { + "through": "2.3.8" + } + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "dev": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "stat-mode": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.2.2.tgz", + "integrity": "sha1-5sgLYjEj19gM8TLOU480YokHJQI=", + "dev": true + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "dev": true, + "requires": { + "duplexer": "0.1.1" + } + }, + "stream-consume": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz", + "integrity": "sha1-pB6tGm1ggc63n2WwYZAbbY89HQ8=", + "dev": true + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "streamfilter": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/streamfilter/-/streamfilter-1.0.5.tgz", + "integrity": "sha1-h1BxEb644phFFxe1Ec/tjwAqv1M=", + "dev": true, + "requires": { + "readable-stream": "2.3.3" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "streamifier": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/streamifier/-/streamifier-0.1.1.tgz", + "integrity": "sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8=", + "dev": true + }, + "string-template": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string-template/-/string-template-1.0.0.tgz", + "integrity": "sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y=", + "dev": true + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "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=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", + "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", + "dev": true, + "requires": { + "first-chunk-stream": "1.0.0", + "is-utf8": "0.2.1" + } + }, + "strip-bom-buf": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz", + "integrity": "sha1-HLRar1dTD0yvhsf3UXnSyaUd1XI=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-bom-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-3.0.0.tgz", + "integrity": "sha1-lWvMXYRDD2klapDtgjdlzYWOFZw=", + "dev": true, + "requires": { + "first-chunk-stream": "2.0.0", + "strip-bom-buf": "1.0.0" + }, + "dependencies": { + "first-chunk-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", + "integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=", + "dev": true, + "requires": { + "readable-stream": "2.3.3" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "4.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "thenify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "dev": true, + "requires": { + "any-promise": "1.3.0" + } + }, + "throat": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-3.2.0.tgz", + "integrity": "sha512-/EY8VpvlqJ+sFtLPeOgc8Pl7kQVOWv0woD87KTXVHPIAE842FGT+rokxIhe8xIUP1cfgrkt0as0vDLjDiMtr8w==", + "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 + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "2.3.3", + "xtend": "4.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "through2-filter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", + "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", + "dev": true, + "requires": { + "through2": "2.0.3", + "xtend": "4.0.1" + } + }, + "throwback": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/throwback/-/throwback-1.1.1.tgz", + "integrity": "sha1-8AfnwXYEptFtegfEGqDo/txhhKQ=", + "dev": true, + "requires": { + "any-promise": "1.3.0" + } + }, + "tildify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz", + "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", + "dev": true, + "requires": { + "os-homedir": "1.0.2" + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true + }, + "to-absolute-glob": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz", + "integrity": "sha1-HN+kcqnvUMI57maZm2YsoOs5k38=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "touch": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-1.0.0.tgz", + "integrity": "sha1-RJy+LbrlqMgDjjDXH6D/RklHxN4=", + "dev": true, + "requires": { + "nopt": "1.0.10" + } + }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "dev": true, + "requires": { + "punycode": "1.4.1" + } + }, + "traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "tslib": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz", + "integrity": "sha512-ymKWWZJST0/CkgduC2qkzjMOWr4bouhuURNXCn/inEX0L57BnRG6FhX76o7FOnsjHazCjfU2LKeSrlS2sIKQJg==", + "dev": true + }, + "tslint": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.7.0.tgz", + "integrity": "sha1-wl4NDJL6EgHCvDDoROCOaCtPNVI=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "colors": "1.1.2", + "commander": "2.11.0", + "diff": "3.3.1", + "glob": "7.1.2", + "minimatch": "3.0.4", + "resolve": "1.4.0", + "semver": "5.4.1", + "tslib": "1.8.0", + "tsutils": "2.12.1" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + } + } + }, + "tsutils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.12.1.tgz", + "integrity": "sha1-9Nlc4zkciXHkblTEzw7bCiHdWyQ=", + "dev": true, + "requires": { + "tslib": "1.8.0" + } + }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", + "dev": true + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.3.tgz", + "integrity": "sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w==", + "dev": true + }, + "typings-core": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/typings-core/-/typings-core-1.6.1.tgz", + "integrity": "sha1-zkspMeovGbuPPay+xpmDrE6WSjc=", + "dev": true, + "requires": { + "any-promise": "1.3.0", + "array-uniq": "1.0.3", + "configstore": "2.1.0", + "debug": "2.6.9", + "detect-indent": "4.0.0", + "graceful-fs": "4.1.11", + "has": "1.0.1", + "invariant": "2.2.2", + "is-absolute": "0.2.6", + "listify": "1.0.0", + "lockfile": "1.0.3", + "make-error-cause": "1.2.2", + "mkdirp": "0.5.1", + "object.pick": "1.3.0", + "parse-json": "2.2.0", + "popsicle": "8.2.0", + "popsicle-proxy-agent": "3.0.0", + "popsicle-retry": "3.2.1", + "popsicle-rewrite": "1.0.0", + "popsicle-status": "2.0.1", + "promise-finally": "2.2.1", + "rc": "1.2.1", + "rimraf": "2.6.2", + "sort-keys": "1.1.2", + "string-template": "1.0.0", + "strip-bom": "2.0.0", + "thenify": "3.3.0", + "throat": "3.2.0", + "touch": "1.0.0", + "typescript": "2.5.3", + "xtend": "4.0.1", + "zip-object": "0.1.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + } + } + }, + "typings-global": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/typings-global/-/typings-global-1.0.20.tgz", + "integrity": "sha512-m3s4wlDTQ1HaGNMNaoWj/N0OjGCzMfU1x+FrPisDLj7DnL6GxdC/nVk9pl0szGwgniDbzoHB0T1C5L/vMUm61w==", + "dev": true, + "requires": { + "semver": "5.4.1", + "smartshell": "1.0.18" + }, + "dependencies": { + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + } + } + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", + "dev": true + }, + "unique-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz", + "integrity": "sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs=", + "dev": true + }, + "untildify": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.2.tgz", + "integrity": "sha1-fx8wIFWz/qDz6B3HjrNnZstl4/E=" + }, + "url-parse": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.9.tgz", + "integrity": "sha1-xn8dd11R8KGJEd17P/rSe7nlvRk=", + "dev": true, + "requires": { + "querystringify": "1.0.0", + "requires-port": "1.0.0" + } + }, + "user-home": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", + "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "1.1.2", + "object.getownpropertydescriptors": "2.0.3" + } + }, + "uuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", + "dev": true + }, + "v8flags": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", + "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", + "dev": true, + "requires": { + "user-home": "1.1.1" + } + }, + "vali-date": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", + "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "dev": true, + "requires": { + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "vinyl": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "dev": true, + "requires": { + "clone": "1.0.2", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + }, + "vinyl-fs": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.14.tgz", + "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", + "dev": true, + "requires": { + "defaults": "1.0.3", + "glob-stream": "3.1.18", + "glob-watcher": "0.0.6", + "graceful-fs": "3.0.11", + "mkdirp": "0.5.1", + "strip-bom": "1.0.0", + "through2": "0.6.5", + "vinyl": "0.4.6" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "dev": true, + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + } + } + }, + "vinyl-source-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-source-stream/-/vinyl-source-stream-1.1.0.tgz", + "integrity": "sha1-RMvlEIIFJ53rDFZTwJSiiHk4sas=", + "dev": true, + "requires": { + "through2": "0.6.5", + "vinyl": "0.4.6" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "dev": true, + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + } + } + }, + "vscode": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/vscode/-/vscode-1.1.5.tgz", + "integrity": "sha1-EOsQQAGEDD3QgTgV/UoF+PyILRQ=", + "dev": true, + "requires": { + "glob": "7.1.2", + "gulp-chmod": "2.0.0", + "gulp-filter": "5.0.1", + "gulp-gunzip": "0.0.3", + "gulp-remote-src": "0.4.3", + "gulp-symdest": "1.1.0", + "gulp-untar": "0.0.6", + "gulp-vinyl-zip": "1.4.0", + "mocha": "3.5.3", + "request": "2.83.0", + "semver": "5.4.1", + "source-map-support": "0.4.18", + "url-parse": "1.1.9", + "vinyl-source-stream": "1.1.0" + }, + "dependencies": { + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "requires": { + "graceful-readlink": "1.0.1" + } + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "diff": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", + "dev": true + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "mocha": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz", + "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.9.0", + "debug": "2.6.8", + "diff": "3.2.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.1", + "growl": "1.9.2", + "he": "1.1.1", + "json3": "3.3.2", + "lodash.create": "3.1.1", + "mkdirp": "0.5.1", + "supports-color": "3.1.2" + }, + "dependencies": { + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + } + } + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, + "supports-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "requires": { + "isexe": "2.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "slide": "1.1.6" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + } + } + }, + "xdg-basedir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz", + "integrity": "sha1-7byQPMOF/ARSPZZqM1UEtVBNG9I=", + "dev": true, + "requires": { + "os-homedir": "1.0.2" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "yauzl": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.8.0.tgz", + "integrity": "sha1-eUUK/yKyqcWkHvVOAtuQfM+/nuI=", + "dev": true, + "requires": { + "buffer-crc32": "0.2.13", + "fd-slicer": "1.0.1" + } + }, + "yazl": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.4.2.tgz", + "integrity": "sha1-FMsZCD4eJacAksFYiqvg9OTdTYg=", + "dev": true, + "requires": { + "buffer-crc32": "0.2.13" + } + }, + "zip-object": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/zip-object/-/zip-object-0.1.0.tgz", + "integrity": "sha1-waDaBMiMg3dW4khoCgP/kC7D9To=", + "dev": true + } + } +} diff --git a/package.json b/package.json index 26fa91b7126..90ac241a2ca 100644 --- a/package.json +++ b/package.json @@ -569,27 +569,27 @@ "dependencies": { "clipboardy": "^1.1.4", "diff-match-patch": "^1.0.0", - "lodash": "^4.12.0", + "lodash": "^4.17.4", "neovim-client": "^2.1.0", - "prettier": "^1.6.1", + "prettier": "^1.7.4", "promised-neovim-client": "^2.0.2", "untildify": "^3.0.2" }, "devDependencies": { - "@types/mocha": "^2.2.39", - "@types/node": "^6.0.41", + "@types/mocha": "^2.2.43", + "@types/node": "^6.0.89", "gulp": "^3.9.1", - "gulp-bump": "^2.1.0", - "gulp-git": "^2.4.1", + "gulp-bump": "^2.8.0", + "gulp-git": "^2.4.2", "gulp-inject-string": "^1.1.0", - "gulp-shell": "^0.5.2", + "gulp-shell": "^0.6.3", "gulp-tag-version": "^1.3.0", - "gulp-tslint": "^7.0.1", - "gulp-typings": "^2.0.0", - "merge-stream": "^1.0.0", - "mocha": "^3.2.0", - "tslint": "^4.3.1", - "typescript": "^2.3.2", + "gulp-tslint": "^8.1.2", + "gulp-typings": "^2.0.4", + "merge-stream": "^1.0.1", + "mocha": "^4.0.1", + "tslint": "^5.7.0", + "typescript": "^2.5.3", "vscode": "^1.1.5" } } diff --git a/src/actions/commands/actions.ts b/src/actions/commands/actions.ts index 0fc72433ce1..64d07586a44 100644 --- a/src/actions/commands/actions.ts +++ b/src/actions/commands/actions.ts @@ -1,3535 +1,3534 @@ -import { RecordedState, VimState } from './../../mode/modeHandler'; -import { SearchState, SearchDirection } from './../../state/searchState'; -import { ReplaceState } from './../../state/replaceState'; -import { VisualBlockMode } from './../../mode/modeVisualBlock'; -import { ModeName } from './../../mode/mode'; -import { Range } from './../../common/motion/range'; -import { TextEditor, EditorScrollByUnit, EditorScrollDirection } from './../../textEditor'; -import { Register, RegisterMode } from './../../register/register'; -import { NumericString } from './../../common/number/numericString'; -import { Position, PositionDiff } from './../../common/motion/position'; -import { Tab, TabCommand } from './../../cmd_line/commands/tab'; -import { Configuration } from './../../configuration/configuration'; -import { allowVSCodeToPropagateCursorUpdatesAndReturnThem } from '../../util'; -import { isTextTransformation } from './../../transformations/transformations'; -import { FileCommand } from './../../cmd_line/commands/file'; -import { QuitCommand } from './../../cmd_line/commands/quit'; -import { OnlyCommand } from './../../cmd_line/commands/only'; -import * as vscode from 'vscode'; -import * as util from './../../util'; -import { RegisterAction } from './../base'; -import * as operator from './../operator'; -import { BaseAction } from './../base'; - -export class DocumentContentChangeAction extends BaseAction { - contentChanges: { - positionDiff: PositionDiff; - textDiff: vscode.TextDocumentContentChangeEvent; - }[] = []; - - public async exec(position: Position, vimState: VimState): Promise { - if (this.contentChanges.length === 0) { - return vimState; - } - - let originalLeftBoundary: vscode.Position; - - if ( - this.contentChanges[0].textDiff.text === '' && - this.contentChanges[0].textDiff.rangeLength === 1 - ) { - originalLeftBoundary = this.contentChanges[0].textDiff.range.end; - } else { - originalLeftBoundary = this.contentChanges[0].textDiff.range.start; - } - - let rightBoundary: vscode.Position = position; - let newStart: vscode.Position | undefined; - let newEnd: vscode.Position | undefined; - - for (let i = 0; i < this.contentChanges.length; i++) { - let contentChange = this.contentChanges[i].textDiff; - - if (contentChange.range.start.line < originalLeftBoundary.line) { - // This change should be ignored - let linesEffected = contentChange.range.end.line - contentChange.range.start.line + 1; - let resultLines = contentChange.text.split('\n').length; - originalLeftBoundary = originalLeftBoundary.with( - originalLeftBoundary.line + resultLines - linesEffected - ); - continue; - } - - if (contentChange.range.start.line === originalLeftBoundary.line) { - newStart = position.with( - position.line, - position.character + contentChange.range.start.character - originalLeftBoundary.character - ); - - if (contentChange.range.end.line === originalLeftBoundary.line) { - newEnd = position.with( - position.line, - position.character + contentChange.range.end.character - originalLeftBoundary.character - ); - } else { - newEnd = position.with( - position.line + contentChange.range.end.line - originalLeftBoundary.line, - contentChange.range.end.character - ); - } - } else { - newStart = position.with( - position.line + contentChange.range.start.line - originalLeftBoundary.line, - contentChange.range.start.character - ); - newEnd = position.with( - position.line + contentChange.range.end.line - originalLeftBoundary.line, - contentChange.range.end.character - ); - } - - if (newStart.isAfter(rightBoundary)) { - // This change should be ignored as it's out of boundary - continue; - } - - // Calculate new right boundary - let newLineCount = contentChange.text.split('\n').length; - let newRightBoundary: vscode.Position; - - if (newLineCount === 1) { - newRightBoundary = newStart.with( - newStart.line, - newStart.character + contentChange.text.length - ); - } else { - newRightBoundary = new vscode.Position( - newStart.line + newLineCount - 1, - contentChange.text.split('\n').pop()!.length - ); - } - - if (newRightBoundary.isAfter(rightBoundary)) { - rightBoundary = newRightBoundary; - } - - vimState.editor.selection = new vscode.Selection(newStart, newEnd); - - if (newStart.isEqual(newEnd)) { - await TextEditor.insert(contentChange.text, Position.FromVSCodePosition(newStart)); - } else { - await TextEditor.replace(vimState.editor.selection, contentChange.text); - } - } - - /** - * We're making an assumption here that content changes are always in order, and I'm not sure - * we're guaranteed that, but it seems to work well enough in practice. - */ - if (newStart && newEnd) { - const last = this.contentChanges[this.contentChanges.length - 1]; - - vimState.cursorStartPosition = Position.FromVSCodePosition(newStart) - .advancePositionByText(last.textDiff.text) - .add(last.positionDiff); - vimState.cursorPosition = Position.FromVSCodePosition(newEnd) - .advancePositionByText(last.textDiff.text) - .add(last.positionDiff); - } - - vimState.currentMode = ModeName.Insert; - return vimState; - } -} - -/** - * A command is something like , :, v, i, etc. - */ -export abstract class BaseCommand extends BaseAction { - /** - * If isCompleteAction is true, then triggering this command is a complete action - - * that means that we'll go and try to run it. - */ - isCompleteAction = true; - - multicursorIndex: number | undefined = undefined; - - /** - * In multi-cursor mode, do we run this command for every cursor, or just once? - */ - public runsOnceForEveryCursor(): boolean { - return true; - } - - /** - * If true, exec() will get called N times where N is the count. - * - * If false, exec() will only be called once, and you are expected to - * handle count prefixes (e.g. the 3 in 3w) yourself. - */ - runsOnceForEachCountPrefix = false; - - canBeRepeatedWithDot = false; - - /** - * Run the command a single time. - */ - public async exec(position: Position, vimState: VimState): Promise { - throw new Error('Not implemented!'); - } - - /** - * Run the command the number of times VimState wants us to. - */ - public async execCount(position: Position, vimState: VimState): Promise { - if (!this.runsOnceForEveryCursor()) { - let timesToRepeat = this.runsOnceForEachCountPrefix ? vimState.recordedState.count || 1 : 1; - - for (let i = 0; i < timesToRepeat; i++) { - vimState = await this.exec(position, vimState); - } - - for (const transformation of vimState.recordedState.transformations) { - if (isTextTransformation(transformation) && transformation.cursorIndex === undefined) { - transformation.cursorIndex = 0; - } - } - - return vimState; - } - - let timesToRepeat = this.runsOnceForEachCountPrefix ? vimState.recordedState.count || 1 : 1; - let resultingCursors: Range[] = []; - let i = 0; - - const cursorsToIterateOver = vimState.allCursors - .map(x => new Range(x.start, x.stop)) - .sort( - (a, b) => - a.start.line > b.start.line || - (a.start.line === b.start.line && a.start.character > b.start.character) - ? 1 - : -1 - ); - - for (const { start, stop } of cursorsToIterateOver) { - this.multicursorIndex = i++; - - vimState.cursorPosition = stop; - vimState.cursorStartPosition = start; - - for (let j = 0; j < timesToRepeat; j++) { - vimState = await this.exec(stop, vimState); - } - - resultingCursors.push(new Range(vimState.cursorStartPosition, vimState.cursorPosition)); - - for (const transformation of vimState.recordedState.transformations) { - if (isTextTransformation(transformation) && transformation.cursorIndex === undefined) { - transformation.cursorIndex = this.multicursorIndex; - } - } - } - - vimState.allCursors = resultingCursors; - - return vimState; - } -} - -// begin actions - -@RegisterAction -export class CommandNumber extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ['']; - isCompleteAction = false; - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - const number = parseInt(this.keysPressed[0], 10); - - vimState.recordedState.count = vimState.recordedState.count * 10 + number; - - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - const isZero = keysPressed[0] === '0'; - - return ( - super.doesActionApply(vimState, keysPressed) && - ((isZero && vimState.recordedState.count > 0) || !isZero) - ); - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - const isZero = keysPressed[0] === '0'; - - return ( - super.couldActionApply(vimState, keysPressed) && - ((isZero && vimState.recordedState.count > 0) || !isZero) - ); - } -} - -@RegisterAction -export class CommandRegister extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ['"', '']; - isCompleteAction = false; - - public async exec(position: Position, vimState: VimState): Promise { - const register = this.keysPressed[1]; - vimState.recordedState.registerName = register; - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - const register = keysPressed[1]; - - return super.doesActionApply(vimState, keysPressed) && Register.isValidRegister(register); - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - const register = keysPressed[1]; - - return super.couldActionApply(vimState, keysPressed) && Register.isValidRegister(register); - } -} - -@RegisterAction -class CommandInsertRegisterContentInSearchMode extends BaseCommand { - modes = [ModeName.SearchInProgressMode]; - keys = ['', '']; - isCompleteAction = false; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.recordedState.registerName = this.keysPressed[1]; - const register = await Register.get(vimState); - let text: string; - - if (register.text instanceof Array) { - text = (register.text as string[]).join('\n'); - } else if (register.text instanceof RecordedState) { - let keyStrokes: string[] = []; - - for (let action of register.text.actionsRun) { - keyStrokes = keyStrokes.concat(action.keysPressed); - } - - text = keyStrokes.join('\n'); - } else { - text = register.text; - } - - if (register.registerMode === RegisterMode.LineWise) { - text += '\n'; - } - - const searchState = vimState.globalState.searchState!; - searchState.searchString += text; - return vimState; - } -} - -@RegisterAction -class CommandRecordMacro extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ['q', '']; - - public async exec(position: Position, vimState: VimState): Promise { - const register = this.keysPressed[1]; - vimState.recordedMacro = new RecordedState(); - vimState.recordedMacro.registerName = register.toLocaleLowerCase(); - - if (!/^[A-Z]+$/.test(register) || !Register.has(register)) { - // If register name is upper case, it means we are appending commands to existing register instead of overriding. - let newRegister = new RecordedState(); - newRegister.registerName = register; - Register.putByKey(newRegister, register); - } - - vimState.isRecordingMacro = true; - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - const register = this.keysPressed[1]; - - return ( - super.doesActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register) - ); - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - const register = this.keysPressed[1]; - - return ( - super.couldActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register) - ); - } -} - -@RegisterAction -export class CommandQuitRecordMacro extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ['q']; - - public async exec(position: Position, vimState: VimState): Promise { - let existingMacro = (await Register.getByKey(vimState.recordedMacro.registerName)) - .text as RecordedState; - existingMacro.actionsRun = existingMacro.actionsRun.concat(vimState.recordedMacro.actionsRun); - vimState.isRecordingMacro = false; - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.doesActionApply(vimState, keysPressed) && vimState.isRecordingMacro; - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.couldActionApply(vimState, keysPressed) && vimState.isRecordingMacro; - } -} - -@RegisterAction -class CommandExecuteMacro extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ['@', '']; - runsOnceForEachCountPrefix = true; - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - const register = this.keysPressed[1]; - vimState.recordedState.transformations.push({ - type: 'macro', - register: register, - replay: 'contentChange', - }); - - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - const register = keysPressed[1]; - - return ( - super.doesActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register) - ); - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - const register = keysPressed[1]; - - return ( - super.couldActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register) - ); - } -} - -@RegisterAction -class CommandExecuteLastMacro extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ['@', '@']; - runsOnceForEachCountPrefix = true; - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - let lastInvokedMacro = vimState.historyTracker.lastInvokedMacro; - - if (lastInvokedMacro) { - vimState.recordedState.transformations.push({ - type: 'macro', - register: lastInvokedMacro.registerName, - replay: 'contentChange', - }); - } - - return vimState; - } -} - -@RegisterAction -class CommandEsc extends BaseCommand { - modes = [ - ModeName.Visual, - ModeName.VisualLine, - ModeName.VisualBlock, - ModeName.Normal, - ModeName.SearchInProgressMode, - ModeName.SurroundInputMode, - ModeName.EasyMotionMode, - ModeName.EasyMotionInputMode, - ]; - keys = [[''], [''], ['']]; - - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - if (vimState.currentMode === ModeName.Normal && !vimState.isMultiCursor) { - // If there's nothing to do on the vim side, we might as well call some - // of vscode's default "close notification" actions. I think we should - // just add to this list as needed. - await vscode.commands.executeCommand('closeReferenceSearchEditor'); - return vimState; - } - - if ( - vimState.currentMode !== ModeName.Visual && - vimState.currentMode !== ModeName.VisualLine && - vimState.currentMode !== ModeName.EasyMotionMode - ) { - // Normally, you don't have to iterate over all cursors, - // as that is handled for you by the state machine. ESC is - // a special case since runsOnceForEveryCursor is false. - - vimState.allCursors = vimState.allCursors.map(x => x.withNewStop(x.stop.getLeft())); - } - - if (vimState.currentMode === ModeName.SearchInProgressMode) { - if (vimState.globalState.searchState) { - vimState.cursorPosition = vimState.globalState.searchState.searchCursorStartPosition; - } - } - - if (vimState.currentMode === ModeName.Normal && vimState.isMultiCursor) { - vimState.isMultiCursor = false; - } - - if (vimState.currentMode === ModeName.EasyMotionMode) { - // Escape or other termination keys were pressed, exit mode - vimState.easyMotion.clearDecorations(); - vimState.currentMode = ModeName.Normal; - } - - // Abort surround operation - if (vimState.currentMode === ModeName.SurroundInputMode) { - vimState.surround = undefined; - } - - vimState.currentMode = ModeName.Normal; - - if (!vimState.isMultiCursor) { - vimState.allCursors = [vimState.allCursors[0]]; - } - - return vimState; - } -} - -@RegisterAction -class CommandEscReplaceMode extends BaseCommand { - modes = [ModeName.Replace]; - keys = [[''], ['']]; - - public async exec(position: Position, vimState: VimState): Promise { - const timesToRepeat = vimState.replaceState!.timesToRepeat; - let textToAdd = ''; - - for (let i = 1; i < timesToRepeat; i++) { - textToAdd += vimState.replaceState!.newChars.join(''); - } - - vimState.recordedState.transformations.push({ - type: 'insertText', - text: textToAdd, - position: position, - diff: new PositionDiff(0, -1), - }); - - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -abstract class CommandEditorScroll extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - runsOnceForEachCountPrefix = false; - keys: string[]; - to: EditorScrollDirection; - by: EditorScrollByUnit; - - public async exec(position: Position, vimState: VimState): Promise { - let timesToRepeat = vimState.recordedState.count || 1; - - vimState.postponedCodeViewChanges.push({ - command: 'editorScroll', - args: { - to: this.to, - by: this.by, - value: timesToRepeat, - revealCursor: true, - select: - [ModeName.Visual, ModeName.VisualBlock, ModeName.VisualLine].indexOf( - vimState.currentMode - ) >= 0, - }, - }); - return vimState; - } -} - -@RegisterAction -class CommandCtrlE extends CommandEditorScroll { - keys = ['']; - to: EditorScrollDirection = 'down'; - by: EditorScrollByUnit = 'line'; -} - -@RegisterAction -class CommandCtrlY extends CommandEditorScroll { - keys = ['']; - to: EditorScrollDirection = 'up'; - by: EditorScrollByUnit = 'line'; -} - -@RegisterAction -class CommandMoveFullPageUp extends CommandEditorScroll { - keys = ['']; - to: EditorScrollDirection = 'up'; - by: EditorScrollByUnit = 'page'; -} - -@RegisterAction -class CommandMoveFullPageDown extends CommandEditorScroll { - keys = ['']; - to: EditorScrollDirection = 'down'; - by: EditorScrollByUnit = 'page'; -} - -@RegisterAction -class CommandMoveHalfPageDown extends CommandEditorScroll { - keys = ['']; - to: EditorScrollDirection = 'down'; - by: EditorScrollByUnit = 'halfPage'; -} - -@RegisterAction -class CommandMoveHalfPageUp extends CommandEditorScroll { - keys = ['']; - to: EditorScrollDirection = 'up'; - by: EditorScrollByUnit = 'halfPage'; -} - -@RegisterAction -export class CommandInsertAtCursor extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['i']; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - // Only allow this command to be prefixed with a count or nothing, no other - // actions or operators before - let previousActionsNumbers = true; - for (const prevAction of vimState.recordedState.actionsRun) { - if (!(prevAction instanceof CommandNumber)) { - previousActionsNumbers = false; - break; - } - } - - if (vimState.recordedState.actionsRun.length === 0 || previousActionsNumbers) { - return super.couldActionApply(vimState, keysPressed); - } - return false; - } -} - -@RegisterAction -class CommandReplaceAtCursor extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['R']; - runsOnceForEachCountPrefix = false; - - public async exec(position: Position, vimState: VimState): Promise { - let timesToRepeat = vimState.recordedState.count || 1; - - vimState.currentMode = ModeName.Replace; - vimState.replaceState = new ReplaceState(position, timesToRepeat); - - return vimState; - } -} - -@RegisterAction -class CommandReplaceInReplaceMode extends BaseCommand { - modes = [ModeName.Replace]; - keys = ['']; - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - const char = this.keysPressed[0]; - const replaceState = vimState.replaceState!; - - if (char === '') { - if (position.isBeforeOrEqual(replaceState.replaceCursorStartPosition)) { - // If you backspace before the beginning of where you started to replace, - // just move the cursor back. - - vimState.cursorPosition = position.getLeft(); - vimState.cursorStartPosition = position.getLeft(); - } else if ( - position.line > replaceState.replaceCursorStartPosition.line || - position.character > replaceState.originalChars.length - ) { - vimState.recordedState.transformations.push({ - type: 'deleteText', - position: position, - }); - } else { - vimState.recordedState.transformations.push({ - type: 'replaceText', - text: replaceState.originalChars[position.character - 1], - start: position.getLeft(), - end: position, - diff: new PositionDiff(0, -1), - }); - } - - replaceState.newChars.pop(); - } else { - if (!position.isLineEnd() && char !== '\n') { - vimState.recordedState.transformations.push({ - type: 'replaceText', - text: char, - start: position, - end: position.getRight(), - diff: new PositionDiff(0, 1), - }); - } else { - vimState.recordedState.transformations.push({ - type: 'insertText', - text: char, - position: position, - }); - } - - replaceState.newChars.push(char); - } - - vimState.currentMode = ModeName.Replace; - return vimState; - } -} - -@RegisterAction -class CommandInsertInSearchMode extends BaseCommand { - modes = [ModeName.SearchInProgressMode]; - keys = [[''], [''], ['']]; - runsOnceForEveryCursor() { - return this.keysPressed[0] === '\n'; - } - - public async exec(position: Position, vimState: VimState): Promise { - const key = this.keysPressed[0]; - const searchState = vimState.globalState.searchState!; - const prevSearchList = vimState.globalState.searchStatePrevious!; - - // handle special keys first - if (key === '' || key === '') { - searchState.searchString = searchState.searchString.slice(0, -1); - } else if (key === '\n') { - vimState.currentMode = vimState.globalState.searchState!.previousMode; - - // Repeat the previous search if no new string is entered - if (searchState.searchString === '') { - if (prevSearchList.length > 0) { - searchState.searchString = prevSearchList[prevSearchList.length - 1].searchString; - } - } - - // Store this search if different than previous - if (vimState.globalState.searchStatePrevious.length !== 0) { - let previousSearchState = vimState.globalState.searchStatePrevious; - if ( - searchState.searchString !== - previousSearchState[previousSearchState.length - 1]!.searchString - ) { - previousSearchState.push(searchState); - } - } else { - vimState.globalState.searchStatePrevious.push(searchState); - } - - // Make sure search history does not exceed configuration option - if (vimState.globalState.searchStatePrevious.length > Configuration.history) { - vimState.globalState.searchStatePrevious.splice(0, 1); - } - - // Update the index to the end of the search history - vimState.globalState.searchStateIndex = vimState.globalState.searchStatePrevious.length - 1; - - // Move cursor to next match - vimState.cursorPosition = searchState.getNextSearchMatchPosition(vimState.cursorPosition).pos; - - return vimState; - } else if (key === '') { - vimState.globalState.searchStateIndex -= 1; - - // Clamp the history index to stay within bounds of search history length - vimState.globalState.searchStateIndex = Math.max(vimState.globalState.searchStateIndex, 0); - - if (prevSearchList[vimState.globalState.searchStateIndex] !== undefined) { - searchState.searchString = - prevSearchList[vimState.globalState.searchStateIndex].searchString; - } - } else if (key === '') { - vimState.globalState.searchStateIndex += 1; - - // If past the first history item, allow user to enter their own search string (not using history) - if ( - vimState.globalState.searchStateIndex > - vimState.globalState.searchStatePrevious.length - 1 - ) { - searchState.searchString = ''; - vimState.globalState.searchStateIndex = vimState.globalState.searchStatePrevious.length; - return vimState; - } - - if (prevSearchList[vimState.globalState.searchStateIndex] !== undefined) { - searchState.searchString = - prevSearchList[vimState.globalState.searchStateIndex].searchString; - } - } else { - searchState.searchString += this.keysPressed[0]; - } - - return vimState; - } -} - -@RegisterAction -class CommandEscInSearchMode extends BaseCommand { - modes = [ModeName.SearchInProgressMode]; - keys = ['']; - runsOnceForEveryCursor() { - return this.keysPressed[0] === '\n'; - } - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Normal; - vimState.globalState.searchState = undefined; - - return vimState; - } -} - -@RegisterAction -class CommandCtrlVInSearchMode extends BaseCommand { - modes = [ModeName.SearchInProgressMode]; - keys = ['']; - runsOnceForEveryCursor() { - return this.keysPressed[0] === '\n'; - } - - public async exec(position: Position, vimState: VimState): Promise { - const searchState = vimState.globalState.searchState!; - const textFromClipboard = util.clipboardPaste(); - - searchState.searchString += textFromClipboard; - return vimState; - } -} - -@RegisterAction -class CommandCmdVInSearchMode extends BaseCommand { - modes = [ModeName.SearchInProgressMode]; - keys = ['']; - runsOnceForEveryCursor() { - return this.keysPressed[0] === '\n'; - } - - public async exec(position: Position, vimState: VimState): Promise { - const searchState = vimState.globalState.searchState!; - const textFromClipboard = util.clipboardPaste(); - - searchState.searchString += textFromClipboard; - return vimState; - } -} - -/** - * Our Vim implementation selects up to but not including the final character - * of a visual selection, instead opting to render a block cursor on the final - * character. This works for everything except VSCode's native copy command, - * which loses the final character because it's not selected. We override that - * copy command here by default to include the final character. - */ -@RegisterAction -class CommandOverrideCopy extends BaseCommand { - modes = [ - ModeName.Visual, - ModeName.VisualLine, - ModeName.VisualBlock, - ModeName.Insert, - ModeName.Normal, - ]; - keys = ['']; // A special key - see ModeHandler - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - let text = ''; - - if (vimState.currentMode === ModeName.Visual || vimState.currentMode === ModeName.Normal) { - text = vimState.allCursors - .map(range => { - const start = Position.EarlierOf(range.start, range.stop); - const stop = Position.LaterOf(range.start, range.stop); - return vimState.editor.document.getText(new vscode.Range(start, stop.getRight())); - }) - .join('\n'); - } else if (vimState.currentMode === ModeName.VisualLine) { - text = vimState.allCursors - .map(range => { - return vimState.editor.document.getText( - new vscode.Range( - Position.EarlierOf(range.start.getLineBegin(), range.stop.getLineBegin()), - Position.LaterOf(range.start.getLineEnd(), range.stop.getLineEnd()) - ) - ); - }) - .join('\n'); - } else if (vimState.currentMode === ModeName.VisualBlock) { - for (const { line } of Position.IterateLine(vimState)) { - text += line + '\n'; - } - } else if (vimState.currentMode === ModeName.Insert) { - text = vimState.editor.selections - .map(selection => { - return vimState.editor.document.getText(new vscode.Range(selection.start, selection.end)); - }) - .join('\n'); - } - - util.clipboardCopy(text); - // all vim yank operations return to normal mode. - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class CommandCmdA extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ['']; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.cursorStartPosition = new Position(0, vimState.desiredColumn); - vimState.cursorPosition = new Position(TextEditor.getLineCount() - 1, vimState.desiredColumn); - vimState.currentMode = ModeName.VisualLine; - - return vimState; - } -} - -function searchCurrentWord( - position: Position, - vimState: VimState, - direction: SearchDirection, - isExact: boolean -) { - const currentWord = TextEditor.getWord(position); - - // If the search is going left then use `getWordLeft()` on position to start - // at the beginning of the word. This ensures that any matches happen - // outside of the currently selected word. - const searchStartCursorPosition = - direction === SearchDirection.Backward - ? vimState.cursorPosition.getWordLeft(true) - : vimState.cursorPosition; - - return createSearchStateAndMoveToMatch({ - needle: currentWord, - vimState, - direction, - isExact, - searchStartCursorPosition, - }); -} - -function searchCurrentSelection(vimState: VimState, direction: SearchDirection) { - const selection = TextEditor.getSelection(); - const end = new Position(selection.end.line, selection.end.character); - const currentSelection = TextEditor.getText(selection.with(selection.start, end)); - - // Go back to Normal mode, otherwise the selection grows to the next match. - vimState.currentMode = ModeName.Normal; - - // If the search is going left then use `getLeft()` on the selection start. - // If going right then use `getRight()` on the selection end. This ensures - // that any matches happen outside of the currently selected word. - const searchStartCursorPosition = - direction === SearchDirection.Backward - ? vimState.lastVisualSelectionStart.getLeft() - : vimState.lastVisualSelectionEnd.getRight(); - - return createSearchStateAndMoveToMatch({ - needle: currentSelection, - vimState, - direction, - isExact: false, - searchStartCursorPosition, - }); -} - -function createSearchStateAndMoveToMatch(args: { - needle?: string | undefined; - vimState: VimState; - direction: SearchDirection; - isExact: boolean; - searchStartCursorPosition: Position; -}) { - const { needle, vimState, isExact } = args; - - if (needle === undefined || needle.length === 0) { - return vimState; - } - - const searchString = isExact ? `\\b${needle}\\b` : needle; - - // Start a search for the given term. - vimState.globalState.searchState = new SearchState( - args.direction, - vimState.cursorPosition, - searchString, - { isRegex: isExact }, - vimState.currentMode - ); - - vimState.cursorPosition = vimState.globalState.searchState.getNextSearchMatchPosition( - args.searchStartCursorPosition - ).pos; - - // Turn one of the highlighting flags back on (turned off with :nohl) - vimState.globalState.hl = true; - - return vimState; -} - -@RegisterAction -class CommandSearchCurrentWordExactForward extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['*']; - isMotion = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - return searchCurrentWord(position, vimState, SearchDirection.Forward, true); - } -} - -@RegisterAction -class CommandSearchCurrentWordForward extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ['g', '*']; - isMotion = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - return searchCurrentWord(position, vimState, SearchDirection.Forward, false); - } -} - -@RegisterAction -class CommandSearchVisualForward extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ['*']; - isMotion = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - return searchCurrentSelection(vimState, SearchDirection.Forward); - } -} - -@RegisterAction -class CommandSearchCurrentWordExactBackward extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['#']; - isMotion = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - return searchCurrentWord(position, vimState, SearchDirection.Backward, true); - } -} - -@RegisterAction -class CommandSearchCurrentWordBackward extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ['g', '#']; - isMotion = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - return searchCurrentWord(position, vimState, SearchDirection.Backward, false); - } -} - -@RegisterAction -class CommandSearchVisualBackward extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ['#']; - isMotion = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - return searchCurrentSelection(vimState, SearchDirection.Backward); - } -} - -@RegisterAction -export class CommandSearchForwards extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ['/']; - isMotion = true; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.globalState.searchState = new SearchState( - SearchDirection.Forward, - vimState.cursorPosition, - '', - { isRegex: true }, - vimState.currentMode - ); - vimState.currentMode = ModeName.SearchInProgressMode; - - // Reset search history index - vimState.globalState.searchStateIndex = vimState.globalState.searchStatePrevious.length; - - vimState.globalState.hl = true; - - return vimState; - } -} - -@RegisterAction -export class CommandSearchBackwards extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ['?']; - isMotion = true; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.globalState.searchState = new SearchState( - SearchDirection.Backward, - vimState.cursorPosition, - '', - { isRegex: true }, - vimState.currentMode - ); - vimState.currentMode = ModeName.SearchInProgressMode; - - // Reset search history index - vimState.globalState.searchStateIndex = vimState.globalState.searchStatePrevious.length; - - vimState.globalState.hl = true; - - return vimState; - } -} - -@RegisterAction -export class MarkCommand extends BaseCommand { - keys = ['m', '']; - modes = [ModeName.Normal]; - - public async exec(position: Position, vimState: VimState): Promise { - const markName = this.keysPressed[1]; - - vimState.historyTracker.addMark(position, markName); - - return vimState; - } -} - -@RegisterAction -export class PutCommand extends BaseCommand { - keys = ['p']; - modes = [ModeName.Normal]; - runsOnceForEachCountPrefix = true; - canBeRepeatedWithDot = true; - - constructor(multicursorIndex?: number) { - super(); - this.multicursorIndex = multicursorIndex; - } - public static async GetText( - vimState: VimState, - multicursorIndex: number | undefined = undefined - ): Promise { - const register = await Register.get(vimState); - - if (vimState.isMultiCursor) { - if (multicursorIndex === undefined) { - console.log('ERROR: no multi cursor index when calling PutCommand#getText'); - - throw new Error('Bad!'); - } - - if (vimState.isMultiCursor && typeof register.text === 'object') { - return register.text[multicursorIndex]; - } - } - - return register.text as string; - } - - public async exec( - position: Position, - vimState: VimState, - after: boolean = false, - adjustIndent: boolean = false - ): Promise { - const register = await Register.get(vimState); - const dest = after ? position : position.getRight(); - - if (register.text instanceof RecordedState) { - /** - * Paste content from recordedState. This one is actually complex as - * Vim has internal key code for key strokes.For example, Backspace - * is stored as `<80>kb`. So if you replay a macro, which is stored - * in a register as `a1<80>kb2`, youshall just get `2` inserted as - * `a` represents entering Insert Mode, `<80>bk` represents - * Backspace. However here, we shall - * insert the plain text content of the register, which is `a1<80>kb2`. - */ - vimState.recordedState.transformations.push({ - type: 'macro', - register: vimState.recordedState.registerName, - replay: 'keystrokes', - }); - return vimState; - } else if (typeof register.text === 'object' && vimState.currentMode === ModeName.VisualBlock) { - return await this.execVisualBlockPaste(register.text, position, vimState, after); - } - - let text = await PutCommand.GetText(vimState, this.multicursorIndex); - - let textToAdd: string; - let whereToAddText: Position; - let diff = new PositionDiff(0, 0); - - if (register.registerMode === RegisterMode.CharacterWise) { - textToAdd = text; - whereToAddText = dest; - } else if ( - vimState.currentMode === ModeName.Visual && - register.registerMode === RegisterMode.LineWise - ) { - // in the specific case of linewise register data during visual mode, - // we need extra newline feeds - textToAdd = '\n' + text + '\n'; - whereToAddText = dest; - } else { - if (adjustIndent) { - // Adjust indent to current line - let indentationWidth = TextEditor.getIndentationLevel(TextEditor.getLineAt(position).text); - let firstLineIdentationWidth = TextEditor.getIndentationLevel(text.split('\n')[0]); - - text = text - .split('\n') - .map(line => { - let currentIdentationWidth = TextEditor.getIndentationLevel(line); - let newIndentationWidth = - currentIdentationWidth - firstLineIdentationWidth + indentationWidth; - - return TextEditor.setIndentationLevel(line, newIndentationWidth); - }) - .join('\n'); - } - - if (after) { - // P insert before current line - textToAdd = text + '\n'; - whereToAddText = dest.getLineBegin(); - } else { - // p paste after current line - textToAdd = '\n' + text; - whereToAddText = dest.getLineEnd(); - } - } - - // More vim weirdness: If the thing you're pasting has a newline, the cursor - // stays in the same place. Otherwise, it moves to the end of what you pasted. - - const numNewlines = text.split('\n').length - 1; - const currentLineLength = TextEditor.getLineAt(position).text.length; - - if (register.registerMode === RegisterMode.LineWise) { - const check = text.match(/^\s*/); - let numWhitespace = 0; - - if (check) { - numWhitespace = check[0].length; - } - - if (after) { - diff = PositionDiff.NewBOLDiff(-numNewlines - 1, numWhitespace); - } else { - diff = PositionDiff.NewBOLDiff(currentLineLength > 0 ? 1 : -numNewlines, numWhitespace); - } - } else { - if (text.indexOf('\n') === -1) { - if (!position.isLineEnd()) { - if (after) { - diff = new PositionDiff(0, -1); - } else { - diff = new PositionDiff(0, textToAdd.length); - } - } - } else { - if (position.isLineEnd()) { - diff = PositionDiff.NewBOLDiff(-numNewlines, position.character); - } else { - if (after) { - diff = PositionDiff.NewBOLDiff(-numNewlines, position.character); - } else { - diff = new PositionDiff(0, 1); - } - } - } - } - - vimState.recordedState.transformations.push({ - type: 'insertText', - text: textToAdd, - position: whereToAddText, - diff: diff, - }); - - vimState.currentRegisterMode = register.registerMode; - return vimState; - } - - private async execVisualBlockPaste( - block: string[], - position: Position, - vimState: VimState, - after: boolean - ): Promise { - if (after) { - position = position.getRight(); - } - - // Add empty lines at the end of the document, if necessary. - let linesToAdd = Math.max(0, block.length - (TextEditor.getLineCount() - position.line) + 1); - - if (linesToAdd > 0) { - await TextEditor.insertAt( - Array(linesToAdd).join('\n'), - new Position( - TextEditor.getLineCount() - 1, - TextEditor.getLineAt(new Position(TextEditor.getLineCount() - 1, 0)).text.length - ) - ); - } - - // paste the entire block. - for (let lineIndex = position.line; lineIndex < position.line + block.length; lineIndex++) { - const line = block[lineIndex - position.line]; - const insertPos = new Position( - lineIndex, - Math.min(position.character, TextEditor.getLineAt(new Position(lineIndex, 0)).text.length) - ); - - await TextEditor.insertAt(line, insertPos); - } - - vimState.currentRegisterMode = RegisterMode.FigureItOutFromCurrentMode; - return vimState; - } - - public async execCount(position: Position, vimState: VimState): Promise { - const result = await super.execCount(position, vimState); - - if ( - vimState.effectiveRegisterMode() === RegisterMode.LineWise && - vimState.recordedState.count > 0 - ) { - const numNewlines = - (await PutCommand.GetText(vimState, this.multicursorIndex)).split('\n').length * - vimState.recordedState.count; - - result.recordedState.transformations.push({ - type: 'moveCursor', - diff: new PositionDiff(-numNewlines + 1, 0), - cursorIndex: this.multicursorIndex, - }); - } - - return result; - } -} - -@RegisterAction -export class GPutCommand extends BaseCommand { - keys = ['g', 'p']; - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - runsOnceForEachCountPrefix = true; - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - const result = await new PutCommand().exec(position, vimState); - - return result; - } - - public async execCount(position: Position, vimState: VimState): Promise { - const register = await Register.get(vimState); - let addedLinesCount: number; - - if (register.text instanceof RecordedState) { - vimState.recordedState.transformations.push({ - type: 'macro', - register: vimState.recordedState.registerName, - replay: 'keystrokes', - }); - - return vimState; - } - if (typeof register.text === 'object') { - // visual block mode - addedLinesCount = register.text.length * vimState.recordedState.count; - } else { - addedLinesCount = register.text.split('\n').length; - } - - const result = await super.execCount(position, vimState); - - if (vimState.effectiveRegisterMode() === RegisterMode.LineWise) { - const line = TextEditor.getLineAt(position).text; - const addAnotherLine = line.length > 0 && addedLinesCount > 1; - - result.recordedState.transformations.push({ - type: 'moveCursor', - diff: PositionDiff.NewBOLDiff(1 + (addAnotherLine ? 1 : 0), 0), - cursorIndex: this.multicursorIndex, - }); - } - - return result; - } -} - -@RegisterAction -export class PutWithIndentCommand extends BaseCommand { - keys = [']', 'p']; - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - runsOnceForEachCountPrefix = true; - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - const result = await new PutCommand().exec(position, vimState, false, true); - return result; - } - - public async execCount(position: Position, vimState: VimState): Promise { - return await super.execCount(position, vimState); - } -} - -@RegisterAction -export class PutCommandVisual extends BaseCommand { - keys = [['p'], ['P']]; - modes = [ModeName.Visual, ModeName.VisualLine]; - runsOnceForEachCountPrefix = true; - - public async exec( - position: Position, - vimState: VimState, - after: boolean = false - ): Promise { - let start = vimState.cursorStartPosition; - let end = vimState.cursorPosition; - const isLineWise = vimState.currentMode === ModeName.VisualLine; - if (start.isAfter(end)) { - [start, end] = [end, start]; - } - - // If the to be inserted text is linewise we have a seperate logic delete the - // selection first than insert - let register = await Register.get(vimState); - if (register.registerMode === RegisterMode.LineWise) { - let result = await new operator.DeleteOperator(this.multicursorIndex).run( - vimState, - start, - end, - false - ); - // to ensure, that the put command nows this is - // an linewise register insertion in visual mode - let oldMode = result.currentMode; - result.currentMode = ModeName.Visual; - result = await new PutCommand().exec(start, result, true); - result.currentMode = oldMode; - return result; - } - - // The reason we need to handle Delete and Yank separately is because of - // linewise mode. If we're in visualLine mode, then we want to copy - // linewise but not necessarily delete linewise. - let result = await new PutCommand(this.multicursorIndex).exec(start, vimState, true); - result.currentRegisterMode = isLineWise ? RegisterMode.LineWise : RegisterMode.CharacterWise; - result.recordedState.registerName = Configuration.useSystemClipboard ? '*' : '"'; - result = await new operator.YankOperator(this.multicursorIndex).run(result, start, end); - result.currentRegisterMode = RegisterMode.CharacterWise; - result = await new operator.DeleteOperator(this.multicursorIndex).run( - result, - start, - end.getLeftIfEOL(), - false - ); - result.currentRegisterMode = RegisterMode.FigureItOutFromCurrentMode; - return result; - } - - // TODO - execWithCount -} - -@RegisterAction -export class PutBeforeCommand extends BaseCommand { - public keys = ['P']; - public modes = [ModeName.Normal]; - canBeRepeatedWithDot = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - const command = new PutCommand(); - command.multicursorIndex = this.multicursorIndex; - - const result = await command.exec(position, vimState, true); - - return result; - } -} - -@RegisterAction -export class GPutBeforeCommand extends BaseCommand { - keys = ['g', 'P']; - modes = [ModeName.Normal]; - - public async exec(position: Position, vimState: VimState): Promise { - const result = await new PutCommand().exec(position, vimState, true); - const register = await Register.get(vimState); - let addedLinesCount: number; - - if (register.text instanceof RecordedState) { - vimState.recordedState.transformations.push({ - type: 'macro', - register: vimState.recordedState.registerName, - replay: 'keystrokes', - }); - - return vimState; - } else if (typeof register.text === 'object') { - // visual block mode - addedLinesCount = register.text.length * vimState.recordedState.count; - } else { - addedLinesCount = register.text.split('\n').length; - } - - if (vimState.effectiveRegisterMode() === RegisterMode.LineWise) { - const line = TextEditor.getLineAt(position).text; - const addAnotherLine = line.length > 0 && addedLinesCount > 1; - - result.recordedState.transformations.push({ - type: 'moveCursor', - diff: PositionDiff.NewBOLDiff(1 + (addAnotherLine ? 1 : 0), 0), - cursorIndex: this.multicursorIndex, - }); - } - - return result; - } -} - -@RegisterAction -export class PutBeforeWithIndentCommand extends BaseCommand { - keys = ['[', 'p']; - modes = [ModeName.Normal]; - - public async exec(position: Position, vimState: VimState): Promise { - const result = await new PutCommand().exec(position, vimState, true, true); - - if (vimState.effectiveRegisterMode() === RegisterMode.LineWise) { - result.cursorPosition = result.cursorPosition - .getPreviousLineBegin() - .getFirstLineNonBlankChar(); - } - - return result; - } -} - -@RegisterAction -class CommandShowCommandLine extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = [':']; - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - vimState.recordedState.transformations.push({ - type: 'showCommandLine', - }); - - if (vimState.currentMode === ModeName.Normal) { - vimState.commandInitialText = ''; - } else { - vimState.commandInitialText = "'<,'>"; - } - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class CommandDot extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['.']; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.recordedState.transformations.push({ - type: 'dot', - }); - - return vimState; - } -} - -abstract class CommandFold extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - commandName: string; - - public async exec(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand(this.commandName); - vimState.currentMode = ModeName.Normal; - return vimState; - } -} - -@RegisterAction -class CommandCloseFold extends CommandFold { - keys = ['z', 'c']; - commandName = 'editor.fold'; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - let timesToRepeat = vimState.recordedState.count || 1; - await vscode.commands.executeCommand('editor.fold', { levels: timesToRepeat, direction: 'up' }); - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - return vimState; - } -} - -@RegisterAction -class CommandCloseAllFolds extends CommandFold { - keys = ['z', 'M']; - commandName = 'editor.foldAll'; -} - -@RegisterAction -class CommandOpenFold extends CommandFold { - keys = ['z', 'o']; - commandName = 'editor.unfold'; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - let timesToRepeat = vimState.recordedState.count || 1; - await vscode.commands.executeCommand('editor.unfold', { - levels: timesToRepeat, - direction: 'up', - }); - - return vimState; - } -} - -@RegisterAction -class CommandOpenAllFolds extends CommandFold { - keys = ['z', 'R']; - commandName = 'editor.unfoldAll'; -} - -@RegisterAction -class CommandCloseAllFoldsRecursively extends CommandFold { - modes = [ModeName.Normal]; - keys = ['z', 'C']; - commandName = 'editor.foldRecursively'; -} - -@RegisterAction -class CommandOpenAllFoldsRecursively extends CommandFold { - modes = [ModeName.Normal]; - keys = ['z', 'O']; - commandName = 'editor.unfoldRecursively'; -} - -@RegisterAction -class CommandCenterScroll extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ['z', 'z']; - - public async exec(position: Position, vimState: VimState): Promise { - // In these modes you want to center on the cursor position - vimState.editor.revealRange( - new vscode.Range(vimState.cursorPosition, vimState.cursorPosition), - vscode.TextEditorRevealType.InCenter - ); - - return vimState; - } -} - -@RegisterAction -class CommandCenterScrollFirstChar extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ['z', '.']; - - public async exec(position: Position, vimState: VimState): Promise { - // In these modes you want to center on the cursor position - // This particular one moves cursor to first non blank char though - vimState.editor.revealRange( - new vscode.Range(vimState.cursorPosition, vimState.cursorPosition), - vscode.TextEditorRevealType.InCenter - ); - - // Move cursor to first char of line - vimState.cursorPosition = vimState.cursorPosition.getFirstLineNonBlankChar(); - - return vimState; - } -} - -@RegisterAction -class CommandTopScroll extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['z', 't']; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: 'revealLine', - args: { - lineNumber: position.line, - at: 'top', - }, - }); - return vimState; - } -} - -@RegisterAction -class CommandTopScrollFirstChar extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ['z', '\n']; - - public async exec(position: Position, vimState: VimState): Promise { - // In these modes you want to center on the cursor position - // This particular one moves cursor to first non blank char though - vimState.postponedCodeViewChanges.push({ - command: 'revealLine', - args: { - lineNumber: position.line, - at: 'top', - }, - }); - - // Move cursor to first char of line - vimState.cursorPosition = vimState.cursorPosition.getFirstLineNonBlankChar(); - - return vimState; - } -} - -@RegisterAction -class CommandBottomScroll extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['z', 'b']; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: 'revealLine', - args: { - lineNumber: position.line, - at: 'bottom', - }, - }); - return vimState; - } -} - -@RegisterAction -class CommandBottomScrollFirstChar extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ['z', '-']; - - public async exec(position: Position, vimState: VimState): Promise { - // In these modes you want to center on the cursor position - // This particular one moves cursor to first non blank char though - vimState.postponedCodeViewChanges.push({ - command: 'revealLine', - args: { - lineNumber: position.line, - at: 'bottom', - }, - }); - - // Move cursor to first char of line - vimState.cursorPosition = vimState.cursorPosition.getFirstLineNonBlankChar(); - - return vimState; - } -} - -@RegisterAction -class CommandGoToOtherEndOfHighlightedText extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ['o']; - - public async exec(position: Position, vimState: VimState): Promise { - [vimState.cursorStartPosition, vimState.cursorPosition] = [ - vimState.cursorPosition, - vimState.cursorStartPosition, - ]; - - return vimState; - } -} - -@RegisterAction -class CommandUndo extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['u']; - runsOnceForEveryCursor() { - return false; - } - mustBeFirstKey = true; - - public async exec(position: Position, vimState: VimState): Promise { - const newPositions = await vimState.historyTracker.goBackHistoryStep(); - - if (newPositions !== undefined) { - vimState.allCursors = newPositions.map(x => new Range(x, x)); - } - - vimState.alteredHistory = true; - return vimState; - } -} - -@RegisterAction -class CommandRedo extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['']; - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - const newPositions = await vimState.historyTracker.goForwardHistoryStep(); - - if (newPositions !== undefined) { - vimState.allCursors = newPositions.map(x => new Range(x, x)); - } - - vimState.alteredHistory = true; - return vimState; - } -} - -@RegisterAction -class CommandDeleteToLineEnd extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['D']; - canBeRepeatedWithDot = true; - runsOnceForEveryCursor() { - return true; - } - - public async exec(position: Position, vimState: VimState): Promise { - if (position.isLineEnd()) { - return vimState; - } - - return await new operator.DeleteOperator(this.multicursorIndex).run( - vimState, - position, - position.getLineEnd().getLeft() - ); - } -} - -@RegisterAction -export class CommandYankFullLine extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['Y']; - - public async exec(position: Position, vimState: VimState): Promise { - const linesDown = (vimState.recordedState.count || 1) - 1; - const start = position.getLineBegin(); - const end = new Position(position.line + linesDown, 0).getLineEnd().getLeft(); - - vimState.currentRegisterMode = RegisterMode.LineWise; - - return await new operator.YankOperator().run(vimState, start, end); - } -} - -@RegisterAction -class CommandChangeToLineEnd extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['C']; - runsOnceForEachCountPrefix = false; - mustBeFirstKey = true; - - public async exec(position: Position, vimState: VimState): Promise { - let count = vimState.recordedState.count || 1; - - return new operator.ChangeOperator().run( - vimState, - position, - position - .getDownByCount(Math.max(0, count - 1)) - .getLineEnd() - .getLeft() - ); - } -} - -@RegisterAction -class CommandClearLine extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['S']; - runsOnceForEachCountPrefix = false; - - public async exec(position: Position, vimState: VimState): Promise { - let count = vimState.recordedState.count || 1; - let end = position - .getDownByCount(Math.max(0, count - 1)) - .getLineEnd() - .getLeft(); - return new operator.ChangeOperator().run( - vimState, - position.getLineBeginRespectingIndent(), - end - ); - } -} - -@RegisterAction -class CommandExitVisualMode extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ['v']; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class CommandVisualMode extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['v']; - isCompleteAction = false; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Visual; - - return vimState; - } -} - -@RegisterAction -class CommandReselectVisual extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['g', 'v']; - - public async exec(position: Position, vimState: VimState): Promise { - // Try to restore selection only if valid - if ( - vimState.lastVisualSelectionEnd !== undefined && - vimState.lastVisualSelectionStart !== undefined && - vimState.lastVisualMode !== undefined - ) { - if (vimState.lastVisualSelectionEnd.line <= TextEditor.getLineCount() - 1) { - vimState.currentMode = vimState.lastVisualMode; - vimState.cursorStartPosition = vimState.lastVisualSelectionStart; - vimState.cursorPosition = vimState.lastVisualSelectionEnd.getLeft(); - } - } - return vimState; - } -} - -@RegisterAction -class CommandVisualBlockMode extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualBlock]; - keys = ['']; - - public async exec(position: Position, vimState: VimState): Promise { - if (vimState.currentMode === ModeName.VisualBlock) { - vimState.currentMode = ModeName.Normal; - } else { - vimState.currentMode = ModeName.VisualBlock; - } - - return vimState; - } -} - -@RegisterAction -class CommandVisualLineMode extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual]; - keys = ['V']; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.VisualLine; - - return vimState; - } -} - -@RegisterAction -class CommandExitVisualLineMode extends BaseCommand { - modes = [ModeName.VisualLine]; - keys = ['V']; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class CommandOpenFile extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual]; - keys = ['g', 'f']; - - public async exec(position: Position, vimState: VimState): Promise { - let fullFilePath: string = ''; - - if (vimState.currentMode === ModeName.Visual) { - const selection = TextEditor.getSelection(); - const end = new Position(selection.end.line, selection.end.character + 1); - fullFilePath = TextEditor.getText(selection.with(selection.start, end)); - } else { - const start = position.getFilePathLeft(true); - const end = position.getFilePathRight(); - const range = new vscode.Range(start, end); - - fullFilePath = TextEditor.getText(range).trim(); - } - const fileInfo = fullFilePath.match(/(.*?(?=:[0-9]+)|.*):?([0-9]*)$/); - if (fileInfo) { - const filePath = fileInfo[1]; - const lineNumber = parseInt(fileInfo[2], 10); - const fileCommand = new FileCommand({ name: filePath, lineNumber: lineNumber }); - fileCommand.execute(); - } - - return vimState; - } -} - -@RegisterAction -class CommandGoToDefinition extends BaseCommand { - modes = [ModeName.Normal]; - keys = [['g', 'd'], ['']]; - - public async exec(position: Position, vimState: VimState): Promise { - const oldActiveEditor = vimState.editor; - - await vscode.commands.executeCommand('editor.action.goToDeclaration'); - - if (oldActiveEditor === vimState.editor) { - vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start); - } - - return vimState; - } -} - -@RegisterAction -class CommandGoBackInChangelist extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['g', ';']; - - public async exec(position: Position, vimState: VimState): Promise { - const originalIndex = vimState.historyTracker.changelistIndex; - const prevPos = vimState.historyTracker.getChangePositionAtindex(originalIndex - 1); - const currPos = vimState.historyTracker.getChangePositionAtindex(originalIndex); - - if (prevPos !== undefined) { - vimState.cursorPosition = prevPos[0]; - vimState.historyTracker.changelistIndex = originalIndex - 1; - } else if (currPos !== undefined) { - vimState.cursorPosition = currPos[0]; - } - - return vimState; - } -} - -@RegisterAction -class CommandGoForwardInChangelist extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['g', ',']; - - public async exec(position: Position, vimState: VimState): Promise { - const originalIndex = vimState.historyTracker.changelistIndex; - const nextPos = vimState.historyTracker.getChangePositionAtindex(originalIndex + 1); - const currPos = vimState.historyTracker.getChangePositionAtindex(originalIndex); - - if (nextPos !== undefined) { - vimState.cursorPosition = nextPos[0]; - vimState.historyTracker.changelistIndex = originalIndex + 1; - } else if (currPos !== undefined) { - vimState.cursorPosition = currPos[0]; - } - - return vimState; - } -} - -@RegisterAction -class CommandGoLastChange extends BaseCommand { - modes = [ModeName.Normal]; - keys = [['`', '.'], ["'", '.']]; - - public async exec(position: Position, vimState: VimState): Promise { - const lastPos = vimState.historyTracker.getLastHistoryStartPosition(); - - if (lastPos !== undefined) { - vimState.cursorPosition = lastPos[0]; - } - - return vimState; - } -} - -@RegisterAction -class CommandInsertAtLastChange extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['g', 'i']; - - public async exec(position: Position, vimState: VimState): Promise { - const lastPos = vimState.historyTracker.getLastChangeEndPosition(); - - if (lastPos !== undefined) { - vimState.cursorPosition = lastPos; - vimState.currentMode = ModeName.Insert; - } - - return vimState; - } -} - -@RegisterAction -export class CommandInsertAtFirstCharacter extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual]; - keys = ['I']; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - vimState.cursorPosition = position.getFirstLineNonBlankChar(); - - return vimState; - } -} - -@RegisterAction -class CommandInsertAtLineBegin extends BaseCommand { - modes = [ModeName.Normal]; - mustBeFirstKey = true; - keys = ['g', 'I']; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - vimState.cursorPosition = position.getLineBegin(); - - return vimState; - } -} - -@RegisterAction -export class CommandInsertAfterCursor extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['a']; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - vimState.cursorPosition = position.getRight(); - - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - // Only allow this command to be prefixed with a count or nothing, no other - // actions or operators before - let previousActionsNumbers = true; - for (const prevAction of vimState.recordedState.actionsRun) { - if (!(prevAction instanceof CommandNumber)) { - previousActionsNumbers = false; - break; - } - } - - if (vimState.recordedState.actionsRun.length === 0 || previousActionsNumbers) { - return super.couldActionApply(vimState, keysPressed); - } - return false; - } -} - -@RegisterAction -export class CommandInsertAtLineEnd extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual]; - keys = ['A']; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - vimState.cursorPosition = position.getLineEnd(); - - return vimState; - } -} - -@RegisterAction -class CommandInsertNewLineAbove extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['O']; - runsOnceForEveryCursor() { - return false; - } - - public async execCount(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - let count = vimState.recordedState.count || 1; - // Why do we do this? Who fucking knows??? If the cursor is at the - // beginning of the line, then editor.action.insertLineBefore does some - // weird things with following paste command. Refer to - // https://github.com/VSCodeVim/Vim/pull/1663#issuecomment-300573129 for - // more details. - const tPos = Position.FromVSCodePosition( - vscode.window.activeTextEditor!.selection.active - ).getRight(); - vscode.window.activeTextEditor!.selection = new vscode.Selection(tPos, tPos); - for (let i = 0; i < count; i++) { - await vscode.commands.executeCommand('editor.action.insertLineBefore'); - } - - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - for (let i = 0; i < count; i++) { - const newPos = new Position( - vimState.allCursors[0].start.line + i, - vimState.allCursors[0].start.character - ); - vimState.allCursors.push(new Range(newPos, newPos)); - } - vimState.allCursors = vimState.allCursors.reverse(); - vimState.isFakeMultiCursor = true; - vimState.isMultiCursor = true; - return vimState; - } -} - -@RegisterAction -class CommandInsertNewLineBefore extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['o']; - runsOnceForEveryCursor() { - return false; - } - - public async execCount(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - let count = vimState.recordedState.count || 1; - - for (let i = 0; i < count; i++) { - await vscode.commands.executeCommand('editor.action.insertLineAfter'); - } - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - for (let i = 1; i < count; i++) { - const newPos = new Position( - vimState.allCursors[0].start.line - i, - vimState.allCursors[0].start.character - ); - vimState.allCursors.push(new Range(newPos, newPos)); - - // Ahhhhhh. We have to manually set cursor position here as we need text - // transformations AND to set multiple cursors. - vimState.recordedState.transformations.push({ - type: 'insertText', - text: TextEditor.setIndentationLevel('', newPos.character), - position: newPos, - cursorIndex: i, - manuallySetCursorPositions: true, - }); - } - vimState.allCursors = vimState.allCursors.reverse(); - vimState.isFakeMultiCursor = true; - vimState.isMultiCursor = true; - return vimState; - } -} - -@RegisterAction -class CommandNavigateBack extends BaseCommand { - modes = [ModeName.Normal]; - keys = [[''], ['']]; - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - const oldActiveEditor = vimState.editor; - - await vscode.commands.executeCommand('workbench.action.navigateBack'); - - if (oldActiveEditor === vimState.editor) { - vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start); - } - - return vimState; - } -} - -@RegisterAction -class CommandNavigateForward extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['']; - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - const oldActiveEditor = vimState.editor; - - await vscode.commands.executeCommand('workbench.action.navigateForward'); - - if (oldActiveEditor === vimState.editor) { - vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start); - } - - return vimState; - } -} - -@RegisterAction -class CommandNavigateLast extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['`', '`']; - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - const oldActiveEditor = vimState.editor; - - await vscode.commands.executeCommand('workbench.action.navigateLast'); - - if (oldActiveEditor === vimState.editor) { - vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start); - } - - return vimState; - } -} - -@RegisterAction -class CommandNavigateLastBOL extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["'", "'"]; - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - const oldActiveEditor = vimState.editor; - - await vscode.commands.executeCommand('workbench.action.navigateLast'); - - if (oldActiveEditor === vimState.editor) { - const pos = Position.FromVSCodePosition(vimState.editor.selection.start); - vimState.cursorPosition = pos.getFirstLineNonBlankChar(); - } - - return vimState; - } -} - -@RegisterAction -class CommandQuit extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['', 'q']; - - public async exec(position: Position, vimState: VimState): Promise { - new QuitCommand({}).execute(); - - return vimState; - } -} - -@RegisterAction -class CommandOnly extends BaseCommand { - modes = [ModeName.Normal]; - keys = [['', 'o'], ['', 'C-o']]; - - public async exec(position: Position, vimState: VimState): Promise { - new OnlyCommand({}).execute(); - - return vimState; - } -} - -@RegisterAction -class MoveToRightPane extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = [['', 'l'], ['', ''], ['']]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: 'workbench.action.navigateRight', - args: {}, - }); - - return vimState; - } -} - -@RegisterAction -class MoveToLowerPane extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = [['', 'j'], ['', ''], ['']]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: 'workbench.action.navigateDown', - args: {}, - }); - - return vimState; - } -} - -@RegisterAction -class MoveToUpperPane extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = [['', 'k'], ['', ''], ['']]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: 'workbench.action.navigateUp', - args: {}, - }); - - return vimState; - } -} - -@RegisterAction -class MoveToLeftPane extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = [['', 'h'], ['', ''], ['']]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: 'workbench.action.navigateLeft', - args: {}, - }); - - return vimState; - } -} - -@RegisterAction -class CycleThroughPanes extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = [['', ''], ['', 'w']]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: 'workbench.action.navigateEditorGroups', - args: {}, - }); - - return vimState; - } -} - -class BaseTabCommand extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - runsOnceForEachCountPrefix = true; -} - -@RegisterAction -class VerticalSplit extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ['', 'v']; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: 'workbench.action.splitEditor', - args: {}, - }); - - return vimState; - } -} - -@RegisterAction -class CommandTabNext extends BaseTabCommand { - keys = [['g', 't'], ['']]; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - new TabCommand({ - tab: Tab.Next, - count: vimState.recordedState.count, - }).execute(); - - return vimState; - } -} - -@RegisterAction -class CommandTabPrevious extends BaseTabCommand { - keys = [['g', 'T'], ['']]; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - new TabCommand({ - tab: Tab.Previous, - count: 1, - }).execute(); - - return vimState; - } -} - -@RegisterAction -class ActionDeleteChar extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['x']; - runsOnceForEachCountPrefix = true; - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - // If line is empty, do nothing - if (TextEditor.getLineAt(position).text.length < 1) { - return vimState; - } - - const state = await new operator.DeleteOperator(this.multicursorIndex).run( - vimState, - position, - position - ); - - state.currentMode = ModeName.Normal; - - return state; - } -} - -@RegisterAction -class ActionDeleteCharWithDeleteKey extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['']; - runsOnceForEachCountPrefix = true; - canBeRepeatedWithDot = true; - - public async execCount(position: Position, vimState: VimState): Promise { - // If has a count in front of it, then deletes a character - // off the count. Therefore, 100x, would apply 'x' 10 times. - // http://vimdoc.sourceforge.net/htmldoc/change.html# - if (vimState.recordedState.count !== 0) { - vimState.recordedState.count = Math.floor(vimState.recordedState.count / 10); - vimState.recordedState.actionKeys = vimState.recordedState.count.toString().split(''); - vimState.recordedState.commandList = vimState.recordedState.count.toString().split(''); - this.isCompleteAction = false; - return vimState; - } - return await new ActionDeleteChar().execCount(position, vimState); - } -} - -@RegisterAction -class ActionDeleteLastChar extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['X']; - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - if (position.character === 0) { - return vimState; - } - - return await new operator.DeleteOperator(this.multicursorIndex).run( - vimState, - position.getLeft(), - position.getLeft() - ); - } -} - -@RegisterAction -class ActionJoin extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['J']; - canBeRepeatedWithDot = true; - runsOnceForEachCountPrefix = false; - - private firstNonWhitespaceIndex(str: string): number { - for (let i = 0, len = str.length; i < len; i++) { - let chCode = str.charCodeAt(i); - if (chCode !== 32 /** space */ && chCode !== 9 /** tab */) { - return i; - } - } - return -1; - } - - public async execJoinLines( - startPosition: Position, - position: Position, - vimState: VimState, - count: number - ): Promise { - count = count - 1 || 1; - - let startLineNumber: number, - startColumn: number, - endLineNumber: number, - endColumn: number, - columnDeltaOffset: number = 0; - - if (startPosition.isEqual(position) || startPosition.line === position.line) { - if (position.line + 1 < TextEditor.getLineCount()) { - startLineNumber = position.line; - startColumn = 0; - endLineNumber = startLineNumber + count; - endColumn = TextEditor.getLineMaxColumn(endLineNumber); - } else { - startLineNumber = position.line; - startColumn = 0; - endLineNumber = position.line; - endColumn = TextEditor.getLineMaxColumn(endLineNumber); - } - } else { - startLineNumber = startPosition.line; - startColumn = 0; - endLineNumber = position.line; - endColumn = TextEditor.getLineMaxColumn(endLineNumber); - } - - let trimmedLinesContent = TextEditor.getLineAt(startPosition).text; - - for (let i = startLineNumber + 1; i <= endLineNumber; i++) { - let lineText = TextEditor.getLineAt(new Position(i, 0)).text; - - let firstNonWhitespaceIdx = this.firstNonWhitespaceIndex(lineText); - - if (firstNonWhitespaceIdx >= 0) { - let insertSpace = true; - - if ( - trimmedLinesContent === '' || - trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === ' ' || - trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === '\t' - ) { - insertSpace = false; - } - - let lineTextWithoutIndent = lineText.substr(firstNonWhitespaceIdx); - - if (lineTextWithoutIndent.charAt(0) === ')') { - insertSpace = false; - } - - trimmedLinesContent += (insertSpace ? ' ' : '') + lineTextWithoutIndent; - - if (insertSpace) { - columnDeltaOffset = lineTextWithoutIndent.length + 1; - } else { - columnDeltaOffset = lineTextWithoutIndent.length; - } - } else { - if ( - trimmedLinesContent === '' || - trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === ' ' || - trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === '\t' - ) { - columnDeltaOffset += 0; - } else { - trimmedLinesContent += ' '; - columnDeltaOffset += 1; - } - } - } - - let deleteStartPosition = new Position(startLineNumber, startColumn); - let deleteEndPosition = new Position(endLineNumber, endColumn); - - if (!deleteStartPosition.isEqual(deleteEndPosition)) { - if (startPosition.isEqual(position)) { - vimState.recordedState.transformations.push({ - type: 'replaceText', - text: trimmedLinesContent, - start: deleteStartPosition, - end: deleteEndPosition, - diff: new PositionDiff( - 0, - trimmedLinesContent.length - columnDeltaOffset - position.character - ), - }); - } else { - vimState.recordedState.transformations.push({ - type: 'replaceText', - text: trimmedLinesContent, - start: deleteStartPosition, - end: deleteEndPosition, - manuallySetCursorPositions: true, - }); - - vimState.cursorPosition = new Position( - startPosition.line, - trimmedLinesContent.length - columnDeltaOffset - ); - vimState.cursorStartPosition = vimState.cursorPosition; - vimState.currentMode = ModeName.Normal; - } - } - - return vimState; - } - - public async execCount(position: Position, vimState: VimState): Promise { - let timesToRepeat = vimState.recordedState.count || 1; - let resultingCursors: Range[] = []; - let i = 0; - - const cursorsToIterateOver = vimState.allCursors - .map(x => new Range(x.start, x.stop)) - .sort( - (a, b) => - a.start.line > b.start.line || - (a.start.line === b.start.line && a.start.character > b.start.character) - ? 1 - : -1 - ); - - for (const { start, stop } of cursorsToIterateOver) { - this.multicursorIndex = i++; - - vimState.cursorPosition = stop; - vimState.cursorStartPosition = start; - - vimState = await this.execJoinLines(start, stop, vimState, timesToRepeat); - - resultingCursors.push(new Range(vimState.cursorStartPosition, vimState.cursorPosition)); - - for (const transformation of vimState.recordedState.transformations) { - if (isTextTransformation(transformation) && transformation.cursorIndex === undefined) { - transformation.cursorIndex = this.multicursorIndex; - } - } - } - - vimState.allCursors = resultingCursors; - - return vimState; - } -} - -@RegisterAction -class ActionJoinVisualMode extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ['J']; - - public async exec(position: Position, vimState: VimState): Promise { - let actionJoin = new ActionJoin(); - let start = Position.FromVSCodePosition(vimState.editor.selection.start); - let end = Position.FromVSCodePosition(vimState.editor.selection.end); - - if (start.isAfter(end)) { - [start, end] = [end, start]; - } - - /** - * For joining lines, Visual Line behaves the same as Visual so we align the register mode here. - */ - vimState.currentRegisterMode = RegisterMode.CharacterWise; - vimState = await actionJoin.execJoinLines(start, end, vimState, 1); - - return vimState; - } -} - -@RegisterAction -class ActionJoinNoWhitespace extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['g', 'J']; - canBeRepeatedWithDot = true; - runsOnceForEachCountPrefix = true; - - // gJ is essentially J without the edge cases. ;-) - - public async exec(position: Position, vimState: VimState): Promise { - if (position.line === TextEditor.getLineCount() - 1) { - return vimState; // TODO: bell - } - - let lineOne = TextEditor.getLineAt(position).text; - let lineTwo = TextEditor.getLineAt(position.getNextLineBegin()).text; - - lineTwo = lineTwo.substring(position.getNextLineBegin().getFirstLineNonBlankChar().character); - - let resultLine = lineOne + lineTwo; - - let newState = await new operator.DeleteOperator(this.multicursorIndex).run( - vimState, - position.getLineBegin(), - lineTwo.length > 0 - ? position - .getNextLineBegin() - .getLineEnd() - .getLeft() - : position.getLineEnd() - ); - - vimState.recordedState.transformations.push({ - type: 'insertText', - text: resultLine, - position: position, - diff: new PositionDiff(0, -lineTwo.length), - }); - - newState.cursorPosition = new Position(position.line, lineOne.length); - - return newState; - } -} - -@RegisterAction -class ActionJoinNoWhitespaceVisualMode extends BaseCommand { - modes = [ModeName.Visual]; - keys = ['g', 'J']; - - public async exec(position: Position, vimState: VimState): Promise { - let actionJoin = new ActionJoinNoWhitespace(); - let start = Position.FromVSCodePosition(vimState.editor.selection.start); - let end = Position.FromVSCodePosition(vimState.editor.selection.end); - - if (start.line === end.line) { - return vimState; - } - - if (start.isAfter(end)) { - [start, end] = [end, start]; - } - - for (let i = start.line; i < end.line; i++) { - vimState = await actionJoin.exec(start, vimState); - } - - return vimState; - } -} - -@RegisterAction -class ActionReplaceCharacter extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['r', '']; - canBeRepeatedWithDot = true; - runsOnceForEachCountPrefix = false; - - public async exec(position: Position, vimState: VimState): Promise { - let timesToRepeat = vimState.recordedState.count || 1; - const toReplace = this.keysPressed[1]; - - /** - * includes , and but not any control keys, - * so we ignore the former two keys and have a special handle for . - */ - - if (['', ''].indexOf(toReplace.toUpperCase()) >= 0) { - return vimState; - } - - if (position.character + timesToRepeat > position.getLineEnd().character) { - return vimState; - } - - let endPos = new Position(position.line, position.character + timesToRepeat); - - // Return if tried to repeat longer than linelength - if (endPos.character > TextEditor.getLineAt(endPos).text.length) { - return vimState; - } - - // If last char (not EOL char), add 1 so that replace selection is complete - if (endPos.character > TextEditor.getLineAt(endPos).text.length) { - endPos = new Position(endPos.line, endPos.character + 1); - } - - if (toReplace === '') { - vimState.recordedState.transformations.push({ - type: 'deleteRange', - range: new Range(position, endPos), - }); - vimState.recordedState.transformations.push({ - type: 'tab', - cursorIndex: this.multicursorIndex, - diff: new PositionDiff(0, -1), - }); - } else { - vimState.recordedState.transformations.push({ - type: 'replaceText', - text: toReplace.repeat(timesToRepeat), - start: position, - end: endPos, - diff: new PositionDiff(0, timesToRepeat - 1), - }); - } - return vimState; - } - - public async execCount(position: Position, vimState: VimState): Promise { - return super.execCount(position, vimState); - } -} - -@RegisterAction -class ActionReplaceCharacterVisual extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ['r', '']; - runsOnceForEveryCursor() { - return false; - } - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - const toInsert = this.keysPressed[1]; - - let visualSelectionOffset = 1; - let start = vimState.cursorStartPosition; - let end = vimState.cursorPosition; - - // If selection is reversed, reorganize it so that the text replace logic always works - if (end.isBeforeOrEqual(start)) { - [start, end] = [end, start]; - } - - // Limit to not replace EOL - const textLength = TextEditor.getLineAt(end).text.length; - if (textLength <= 0) { - visualSelectionOffset = 0; - } - end = new Position(end.line, Math.min(end.character, textLength > 0 ? textLength - 1 : 0)); - - // Iterate over every line in the current selection - for (var lineNum = start.line; lineNum <= end.line; lineNum++) { - // Get line of text - const lineText = TextEditor.getLineAt(new Position(lineNum, 0)).text; - - if (start.line === end.line) { - // This is a visual section all on one line, only replace the part within the selection - vimState.recordedState.transformations.push({ - type: 'replaceText', - text: Array(end.character - start.character + 2).join(toInsert), - start: start, - end: new Position(end.line, end.character + 1), - manuallySetCursorPositions: true, - }); - } else if (lineNum === start.line) { - // This is the first line of the selection so only replace after the cursor - vimState.recordedState.transformations.push({ - type: 'replaceText', - text: Array(lineText.length - start.character + 1).join(toInsert), - start: start, - end: new Position(start.line, lineText.length), - manuallySetCursorPositions: true, - }); - } else if (lineNum === end.line) { - // This is the last line of the selection so only replace before the cursor - vimState.recordedState.transformations.push({ - type: 'replaceText', - text: Array(end.character + 1 + visualSelectionOffset).join(toInsert), - start: new Position(end.line, 0), - end: new Position(end.line, end.character + visualSelectionOffset), - manuallySetCursorPositions: true, - }); - } else { - // Replace the entire line length since it is in the middle of the selection - vimState.recordedState.transformations.push({ - type: 'replaceText', - text: Array(lineText.length + 1).join(toInsert), - start: new Position(lineNum, 0), - end: new Position(lineNum, lineText.length), - manuallySetCursorPositions: true, - }); - } - } - - vimState.cursorPosition = start; - vimState.cursorStartPosition = start; - vimState.currentMode = ModeName.Normal; - return vimState; - } -} - -@RegisterAction -class ActionReplaceCharacterVisualBlock extends BaseCommand { - modes = [ModeName.VisualBlock]; - keys = ['r', '']; - runsOnceForEveryCursor() { - return false; - } - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - const toInsert = this.keysPressed[1]; - for (const { start, end } of Position.IterateLine(vimState)) { - if (end.isBeforeOrEqual(start)) { - continue; - } - - vimState.recordedState.transformations.push({ - type: 'replaceText', - text: Array(end.character - start.character + 1).join(toInsert), - start: start, - end: end, - manuallySetCursorPositions: true, - }); - } - - const topLeft = VisualBlockMode.getTopLeftPosition( - vimState.cursorPosition, - vimState.cursorStartPosition - ); - vimState.allCursors = [new Range(topLeft, topLeft)]; - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class ActionXVisualBlock extends BaseCommand { - modes = [ModeName.VisualBlock]; - keys = ['x']; - canBeRepeatedWithDot = true; - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - for (const { start, end } of Position.IterateLine(vimState)) { - vimState.recordedState.transformations.push({ - type: 'deleteRange', - range: new Range(start, end), - manuallySetCursorPositions: true, - }); - } - - const topLeft = VisualBlockMode.getTopLeftPosition( - vimState.cursorPosition, - vimState.cursorStartPosition - ); - - vimState.allCursors = [new Range(topLeft, topLeft)]; - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class ActionDVisualBlock extends ActionXVisualBlock { - modes = [ModeName.VisualBlock]; - keys = ['d']; - canBeRepeatedWithDot = true; - runsOnceForEveryCursor() { - return false; - } -} - -@RegisterAction -class ActionShiftDVisualBlock extends BaseCommand { - modes = [ModeName.VisualBlock]; - keys = ['D']; - canBeRepeatedWithDot = true; - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - for (const { start } of Position.IterateLine(vimState)) { - vimState.recordedState.transformations.push({ - type: 'deleteRange', - range: new Range(start, start.getLineEnd()), - manuallySetCursorPositions: true, - }); - } - - const topLeft = VisualBlockMode.getTopLeftPosition( - vimState.cursorPosition, - vimState.cursorStartPosition - ); - - vimState.allCursors = [new Range(topLeft, topLeft)]; - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class ActionGoToInsertVisualBlockMode extends BaseCommand { - modes = [ModeName.VisualBlock]; - keys = ['I']; - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - vimState.isMultiCursor = true; - vimState.isFakeMultiCursor = true; - - for (const { line, start } of Position.IterateLine(vimState)) { - if (line === '' && start.character !== 0) { - continue; - } - vimState.allCursors.push(new Range(start, start)); - } - vimState.allCursors = vimState.allCursors.slice(1); - return vimState; - } -} - -@RegisterAction -class ActionChangeInVisualBlockMode extends BaseCommand { - modes = [ModeName.VisualBlock]; - keys = [['c'], ['s']]; - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - for (const { start, end } of Position.IterateLine(vimState)) { - vimState.recordedState.transformations.push({ - type: 'deleteRange', - range: new Range(start, end), - manuallySetCursorPositions: true, - }); - } - - vimState.currentMode = ModeName.Insert; - vimState.isMultiCursor = true; - vimState.isFakeMultiCursor = true; - - for (const { start } of Position.IterateLine(vimState)) { - vimState.allCursors.push(new Range(start, start)); - } - vimState.allCursors = vimState.allCursors.slice(1); - - return vimState; - } -} - -@RegisterAction -class ActionChangeToEOLInVisualBlockMode extends BaseCommand { - modes = [ModeName.VisualBlock]; - keys = [['C'], ['S']]; - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - for (const { start } of Position.IterateLine(vimState)) { - vimState.recordedState.transformations.push({ - type: 'deleteRange', - range: new Range(start, start.getLineEnd()), - collapseRange: true, - }); - } - - vimState.currentMode = ModeName.Insert; - vimState.isMultiCursor = true; - vimState.isFakeMultiCursor = true; - - for (const { end } of Position.IterateLine(vimState)) { - vimState.allCursors.push(new Range(end, end)); - } - vimState.allCursors = vimState.allCursors.slice(1); - - return vimState; - } -} - -@RegisterAction -class ActionGoToInsertVisualBlockModeAppend extends BaseCommand { - modes = [ModeName.VisualBlock]; - keys = ['A']; - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - vimState.isMultiCursor = true; - vimState.isFakeMultiCursor = true; - - for (const { line, end } of Position.IterateLine(vimState)) { - if (line.trim() === '') { - vimState.recordedState.transformations.push({ - type: 'replaceText', - text: TextEditor.setIndentationLevel(line, end.character), - start: new Position(end.line, 0), - end: new Position(end.line, end.character), - position: new Position(end.line, 0), - }); - } - vimState.allCursors.push(new Range(end, end)); - } - vimState.allCursors = vimState.allCursors.slice(1); - return vimState; - } -} - -@RegisterAction -class ActionDeleteLineVisualMode extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ['X']; - - public async exec(position: Position, vimState: VimState): Promise { - if (vimState.currentMode === ModeName.Visual) { - return await new operator.DeleteOperator(this.multicursorIndex).run( - vimState, - vimState.cursorStartPosition.getLineBegin(), - vimState.cursorPosition.getLineEnd() - ); - } else { - return await new operator.DeleteOperator(this.multicursorIndex).run( - vimState, - position.getLineBegin(), - position.getLineEnd() - ); - } - } -} - -@RegisterAction -class ActionChangeLineVisualMode extends BaseCommand { - modes = [ModeName.Visual]; - keys = ['C']; - - public async exec(position: Position, vimState: VimState): Promise { - return await new operator.DeleteOperator(this.multicursorIndex).run( - vimState, - vimState.cursorStartPosition.getLineBegin(), - vimState.cursorPosition.getLineEndIncludingEOL() - ); - } -} - -@RegisterAction -class ActionRemoveLineVisualMode extends BaseCommand { - modes = [ModeName.Visual]; - keys = ['R']; - - public async exec(position: Position, vimState: VimState): Promise { - return await new operator.DeleteOperator(this.multicursorIndex).run( - vimState, - vimState.cursorStartPosition.getLineBegin(), - vimState.cursorPosition.getLineEndIncludingEOL() - ); - } -} - -@RegisterAction -class ActionChangeChar extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['s']; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - const state = await new operator.ChangeOperator().run(vimState, position, position); - - state.currentMode = ModeName.Insert; - - return state; - } - - // Don't clash with surround mode! - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.doesActionApply(vimState, keysPressed) && !vimState.recordedState.operator; - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.doesActionApply(vimState, keysPressed) && !vimState.recordedState.operator; - } -} - -@RegisterAction -class ToggleCaseAndMoveForward extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['~']; - canBeRepeatedWithDot = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - await new operator.ToggleCaseOperator().run( - vimState, - vimState.cursorPosition, - vimState.cursorPosition - ); - - vimState.cursorPosition = vimState.cursorPosition.getRight(); - return vimState; - } -} - -abstract class IncrementDecrementNumberAction extends BaseCommand { - modes = [ModeName.Normal]; - canBeRepeatedWithDot = true; - offset: number; - - public async exec(position: Position, vimState: VimState): Promise { - const text = TextEditor.getLineAt(position).text; - - // Start looking to the right for the next word to increment, unless we're - // already on a word to increment, in which case start at the beginning of - // that word. - const whereToStart = text[position.character].match(/\s/) - ? position - : position.getWordLeft(true); - - for (let { start, end, word } of Position.IterateWords(whereToStart)) { - // '-' doesn't count as a word, but is important to include in parsing - // the number - if (text[start.character - 1] === '-') { - start = start.getLeft(); - word = text[start.character] + word; - } - // Strict number parsing so "1a" doesn't silently get converted to "1" - do { - const num = NumericString.parse(word); - if ( - num !== null && - position.character < start.character + num.prefix.length + num.value.toString().length - ) { - vimState.cursorPosition = await this.replaceNum( - num, - this.offset * (vimState.recordedState.count || 1), - start, - end - ); - vimState.cursorPosition = vimState.cursorPosition.getLeftByCount(num.suffix.length); - return vimState; - } else if (num !== null) { - word = word.slice(num.prefix.length + num.value.toString().length); - start = new Position( - start.line, - start.character + num.prefix.length + num.value.toString().length - ); - } else { - break; - } - } while (true); - } - // No usable numbers, return the original position - return vimState; - } - - public async replaceNum( - start: NumericString, - offset: number, - startPos: Position, - endPos: Position - ): Promise { - const oldWidth = start.toString().length; - start.value += offset; - const newNum = start.toString(); - - const range = new vscode.Range(startPos, endPos.getRight()); - - if (oldWidth === newNum.length) { - await TextEditor.replace(range, newNum); - } else { - // Can't use replace, since new number is a different width than old - await TextEditor.delete(range); - await TextEditor.insertAt(newNum, startPos); - // Adjust end position according to difference in width of number-string - endPos = new Position(endPos.line, endPos.character + (newNum.length - oldWidth)); - } - - return endPos; - } -} - -@RegisterAction -class IncrementNumberAction extends IncrementDecrementNumberAction { - keys = ['']; - offset = +1; -} - -@RegisterAction -class DecrementNumberAction extends IncrementDecrementNumberAction { - keys = ['']; - offset = -1; -} - -@RegisterAction -class ActionTriggerHover extends BaseCommand { - modes = [ModeName.Normal]; - keys = ['g', 'h']; - runsOnceForEveryCursor() { - return false; - } - - public async exec(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand('editor.action.showHover'); - - return vimState; - } -} - -/** - * Multi-Cursor Command Overrides - * - * We currently have to override the vscode key commands that get us into multi-cursor mode. - * - * Normally, we'd just listen for another cursor to be added in order to go into multi-cursor - * mode rather than rewriting each keybinding one-by-one. We can't currently do that because - * Visual Block Mode also creates additional cursors, but will get confused if you're in - * multi-cursor mode. - */ - -@RegisterAction -class ActionOverrideCmdD extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual]; - keys = [[''], ['g', 'b']]; - runsOnceForEveryCursor() { - return false; - } - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand('editor.action.addSelectionToNextFindMatch'); - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - - // If this is the first cursor, select 1 character less - // so that only the word is selected, no extra character - vimState.allCursors = vimState.allCursors.map(x => x.withNewStop(x.stop.getLeft())); - - vimState.currentMode = ModeName.Visual; - - return vimState; - } -} - -@RegisterAction -class ActionOverrideCmdDInsert extends BaseCommand { - modes = [ModeName.Insert]; - keys = ['']; - runsOnceForEveryCursor() { - return false; - } - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - // Since editor.action.addSelectionToNextFindMatch uses the selection to - // determine where to add a word, we need to do a hack and manually set the - // selections to the word boundaries before we make the api call. - vscode.window.activeTextEditor!.selections = vscode.window - .activeTextEditor!.selections.map((x, idx) => { - const curPos = Position.FromVSCodePosition(x.active); - if (idx === 0) { - return new vscode.Selection( - curPos.getWordLeft(false), - curPos - .getLeft() - .getCurrentWordEnd(true) - .getRight() - ); - } else { - // Since we're adding the selections ourselves, we need to make sure - // that our selection is actually over what our original word is - const matchWordPos = Position.FromVSCodePosition( - vscode.window.activeTextEditor!.selections[0].active - ); - const matchWordLength = - matchWordPos - .getLeft() - .getCurrentWordEnd(true) - .getRight().character - matchWordPos.getWordLeft(false).character; - const wordBegin = curPos.getLeftByCount(matchWordLength); - return new vscode.Selection(wordBegin, curPos); - } - }); - await vscode.commands.executeCommand('editor.action.addSelectionToNextFindMatch'); - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - return vimState; - } -} - -@RegisterAction -class ActionOverrideCmdAltDown extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual]; - keys = [ - [''], // OSX - [''], // Windows - ]; - runsOnceForEveryCursor() { - return false; - } - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand('editor.action.insertCursorBelow'); - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - - return vimState; - } -} - -@RegisterAction -class ActionOverrideCmdAltUp extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual]; - keys = [ - [''], // OSX - [''], // Windows - ]; - runsOnceForEveryCursor() { - return false; - } - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand('editor.action.insertCursorAbove'); - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - - return vimState; - } -} +import { RecordedState, VimState } from './../../mode/modeHandler'; +import { SearchState, SearchDirection } from './../../state/searchState'; +import { ReplaceState } from './../../state/replaceState'; +import { VisualBlockMode } from './../../mode/modeVisualBlock'; +import { ModeName } from './../../mode/mode'; +import { Range } from './../../common/motion/range'; +import { TextEditor, EditorScrollByUnit, EditorScrollDirection } from './../../textEditor'; +import { Register, RegisterMode } from './../../register/register'; +import { NumericString } from './../../common/number/numericString'; +import { Position, PositionDiff } from './../../common/motion/position'; +import { Tab, TabCommand } from './../../cmd_line/commands/tab'; +import { Configuration } from './../../configuration/configuration'; +import { allowVSCodeToPropagateCursorUpdatesAndReturnThem } from '../../util'; +import { isTextTransformation } from './../../transformations/transformations'; +import { FileCommand } from './../../cmd_line/commands/file'; +import { QuitCommand } from './../../cmd_line/commands/quit'; +import { OnlyCommand } from './../../cmd_line/commands/only'; +import * as vscode from 'vscode'; +import * as util from './../../util'; +import { RegisterAction } from './../base'; +import * as operator from './../operator'; +import { BaseAction } from './../base'; + +export class DocumentContentChangeAction extends BaseAction { + contentChanges: { + positionDiff: PositionDiff; + textDiff: vscode.TextDocumentContentChangeEvent; + }[] = []; + + public async exec(position: Position, vimState: VimState): Promise { + if (this.contentChanges.length === 0) { + return vimState; + } + + let originalLeftBoundary: vscode.Position; + + if ( + this.contentChanges[0].textDiff.text === '' && + this.contentChanges[0].textDiff.rangeLength === 1 + ) { + originalLeftBoundary = this.contentChanges[0].textDiff.range.end; + } else { + originalLeftBoundary = this.contentChanges[0].textDiff.range.start; + } + + let rightBoundary: vscode.Position = position; + let newStart: vscode.Position | undefined; + let newEnd: vscode.Position | undefined; + + for (let i = 0; i < this.contentChanges.length; i++) { + let contentChange = this.contentChanges[i].textDiff; + + if (contentChange.range.start.line < originalLeftBoundary.line) { + // This change should be ignored + let linesEffected = contentChange.range.end.line - contentChange.range.start.line + 1; + let resultLines = contentChange.text.split('\n').length; + originalLeftBoundary = originalLeftBoundary.with( + originalLeftBoundary.line + resultLines - linesEffected + ); + continue; + } + + if (contentChange.range.start.line === originalLeftBoundary.line) { + newStart = position.with( + position.line, + position.character + contentChange.range.start.character - originalLeftBoundary.character + ); + + if (contentChange.range.end.line === originalLeftBoundary.line) { + newEnd = position.with( + position.line, + position.character + contentChange.range.end.character - originalLeftBoundary.character + ); + } else { + newEnd = position.with( + position.line + contentChange.range.end.line - originalLeftBoundary.line, + contentChange.range.end.character + ); + } + } else { + newStart = position.with( + position.line + contentChange.range.start.line - originalLeftBoundary.line, + contentChange.range.start.character + ); + newEnd = position.with( + position.line + contentChange.range.end.line - originalLeftBoundary.line, + contentChange.range.end.character + ); + } + + if (newStart.isAfter(rightBoundary)) { + // This change should be ignored as it's out of boundary + continue; + } + + // Calculate new right boundary + let newLineCount = contentChange.text.split('\n').length; + let newRightBoundary: vscode.Position; + + if (newLineCount === 1) { + newRightBoundary = newStart.with( + newStart.line, + newStart.character + contentChange.text.length + ); + } else { + newRightBoundary = new vscode.Position( + newStart.line + newLineCount - 1, + contentChange.text.split('\n').pop()!.length + ); + } + + if (newRightBoundary.isAfter(rightBoundary)) { + rightBoundary = newRightBoundary; + } + + vimState.editor.selection = new vscode.Selection(newStart, newEnd); + + if (newStart.isEqual(newEnd)) { + await TextEditor.insert(contentChange.text, Position.FromVSCodePosition(newStart)); + } else { + await TextEditor.replace(vimState.editor.selection, contentChange.text); + } + } + + /** + * We're making an assumption here that content changes are always in order, and I'm not sure + * we're guaranteed that, but it seems to work well enough in practice. + */ + if (newStart && newEnd) { + const last = this.contentChanges[this.contentChanges.length - 1]; + + vimState.cursorStartPosition = Position.FromVSCodePosition(newStart) + .advancePositionByText(last.textDiff.text) + .add(last.positionDiff); + vimState.cursorPosition = Position.FromVSCodePosition(newEnd) + .advancePositionByText(last.textDiff.text) + .add(last.positionDiff); + } + + vimState.currentMode = ModeName.Insert; + return vimState; + } +} + +/** + * A command is something like , :, v, i, etc. + */ +export abstract class BaseCommand extends BaseAction { + /** + * If isCompleteAction is true, then triggering this command is a complete action - + * that means that we'll go and try to run it. + */ + isCompleteAction = true; + + multicursorIndex: number | undefined = undefined; + + /** + * In multi-cursor mode, do we run this command for every cursor, or just once? + */ + public runsOnceForEveryCursor(): boolean { + return true; + } + + /** + * If true, exec() will get called N times where N is the count. + * + * If false, exec() will only be called once, and you are expected to + * handle count prefixes (e.g. the 3 in 3w) yourself. + */ + runsOnceForEachCountPrefix = false; + + canBeRepeatedWithDot = false; + + /** + * Run the command a single time. + */ + public async exec(position: Position, vimState: VimState): Promise { + throw new Error('Not implemented!'); + } + + /** + * Run the command the number of times VimState wants us to. + */ + public async execCount(position: Position, vimState: VimState): Promise { + let timesToRepeat = this.runsOnceForEachCountPrefix ? vimState.recordedState.count || 1 : 1; + + if (!this.runsOnceForEveryCursor()) { + for (let i = 0; i < timesToRepeat; i++) { + vimState = await this.exec(position, vimState); + } + + for (const transformation of vimState.recordedState.transformations) { + if (isTextTransformation(transformation) && transformation.cursorIndex === undefined) { + transformation.cursorIndex = 0; + } + } + + return vimState; + } + + let resultingCursors: Range[] = []; + + const cursorsToIterateOver = vimState.allCursors + .map(x => new Range(x.start, x.stop)) + .sort( + (a, b) => + a.start.line > b.start.line || + (a.start.line === b.start.line && a.start.character > b.start.character) + ? 1 + : -1 + ); + + let cursorIndex = 0; + for (const { start, stop } of cursorsToIterateOver) { + this.multicursorIndex = cursorIndex++; + + vimState.cursorPosition = stop; + vimState.cursorStartPosition = start; + + for (let j = 0; j < timesToRepeat; j++) { + vimState = await this.exec(stop, vimState); + } + + resultingCursors.push(new Range(vimState.cursorStartPosition, vimState.cursorPosition)); + + for (const transformation of vimState.recordedState.transformations) { + if (isTextTransformation(transformation) && transformation.cursorIndex === undefined) { + transformation.cursorIndex = this.multicursorIndex; + } + } + } + + vimState.allCursors = resultingCursors; + + return vimState; + } +} + +// begin actions + +@RegisterAction +export class CommandNumber extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + keys = ['']; + isCompleteAction = false; + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + const number = parseInt(this.keysPressed[0], 10); + + vimState.recordedState.count = vimState.recordedState.count * 10 + number; + + return vimState; + } + + public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { + const isZero = keysPressed[0] === '0'; + + return ( + super.doesActionApply(vimState, keysPressed) && + ((isZero && vimState.recordedState.count > 0) || !isZero) + ); + } + + public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { + const isZero = keysPressed[0] === '0'; + + return ( + super.couldActionApply(vimState, keysPressed) && + ((isZero && vimState.recordedState.count > 0) || !isZero) + ); + } +} + +@RegisterAction +export class CommandRegister extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ['"', '']; + isCompleteAction = false; + + public async exec(position: Position, vimState: VimState): Promise { + const register = this.keysPressed[1]; + vimState.recordedState.registerName = register; + return vimState; + } + + public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { + const register = keysPressed[1]; + + return super.doesActionApply(vimState, keysPressed) && Register.isValidRegister(register); + } + + public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { + const register = keysPressed[1]; + + return super.couldActionApply(vimState, keysPressed) && Register.isValidRegister(register); + } +} + +@RegisterAction +class CommandInsertRegisterContentInSearchMode extends BaseCommand { + modes = [ModeName.SearchInProgressMode]; + keys = ['', '']; + isCompleteAction = false; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.recordedState.registerName = this.keysPressed[1]; + const register = await Register.get(vimState); + let text: string; + + if (register.text instanceof Array) { + text = (register.text as string[]).join('\n'); + } else if (register.text instanceof RecordedState) { + let keyStrokes: string[] = []; + + for (let action of register.text.actionsRun) { + keyStrokes = keyStrokes.concat(action.keysPressed); + } + + text = keyStrokes.join('\n'); + } else { + text = register.text; + } + + if (register.registerMode === RegisterMode.LineWise) { + text += '\n'; + } + + const searchState = vimState.globalState.searchState!; + searchState.searchString += text; + return vimState; + } +} + +@RegisterAction +class CommandRecordMacro extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ['q', '']; + + public async exec(position: Position, vimState: VimState): Promise { + const register = this.keysPressed[1]; + vimState.recordedMacro = new RecordedState(); + vimState.recordedMacro.registerName = register.toLocaleLowerCase(); + + if (!/^[A-Z]+$/.test(register) || !Register.has(register)) { + // If register name is upper case, it means we are appending commands to existing register instead of overriding. + let newRegister = new RecordedState(); + newRegister.registerName = register; + Register.putByKey(newRegister, register); + } + + vimState.isRecordingMacro = true; + return vimState; + } + + public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { + const register = this.keysPressed[1]; + + return ( + super.doesActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register) + ); + } + + public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { + const register = this.keysPressed[1]; + + return ( + super.couldActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register) + ); + } +} + +@RegisterAction +export class CommandQuitRecordMacro extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ['q']; + + public async exec(position: Position, vimState: VimState): Promise { + let existingMacro = (await Register.getByKey(vimState.recordedMacro.registerName)) + .text as RecordedState; + existingMacro.actionsRun = existingMacro.actionsRun.concat(vimState.recordedMacro.actionsRun); + vimState.isRecordingMacro = false; + return vimState; + } + + public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { + return super.doesActionApply(vimState, keysPressed) && vimState.isRecordingMacro; + } + + public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { + return super.couldActionApply(vimState, keysPressed) && vimState.isRecordingMacro; + } +} + +@RegisterAction +class CommandExecuteMacro extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ['@', '']; + runsOnceForEachCountPrefix = true; + canBeRepeatedWithDot = true; + + public async exec(position: Position, vimState: VimState): Promise { + const register = this.keysPressed[1]; + vimState.recordedState.transformations.push({ + type: 'macro', + register: register, + replay: 'contentChange', + }); + + return vimState; + } + + public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { + const register = keysPressed[1]; + + return ( + super.doesActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register) + ); + } + + public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { + const register = keysPressed[1]; + + return ( + super.couldActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register) + ); + } +} + +@RegisterAction +class CommandExecuteLastMacro extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ['@', '@']; + runsOnceForEachCountPrefix = true; + canBeRepeatedWithDot = true; + + public async exec(position: Position, vimState: VimState): Promise { + let lastInvokedMacro = vimState.historyTracker.lastInvokedMacro; + + if (lastInvokedMacro) { + vimState.recordedState.transformations.push({ + type: 'macro', + register: lastInvokedMacro.registerName, + replay: 'contentChange', + }); + } + + return vimState; + } +} + +@RegisterAction +class CommandEsc extends BaseCommand { + modes = [ + ModeName.Visual, + ModeName.VisualLine, + ModeName.VisualBlock, + ModeName.Normal, + ModeName.SearchInProgressMode, + ModeName.SurroundInputMode, + ModeName.EasyMotionMode, + ModeName.EasyMotionInputMode, + ]; + keys = [[''], [''], ['']]; + + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + if (vimState.currentMode === ModeName.Normal && !vimState.isMultiCursor) { + // If there's nothing to do on the vim side, we might as well call some + // of vscode's default "close notification" actions. I think we should + // just add to this list as needed. + await vscode.commands.executeCommand('closeReferenceSearchEditor'); + return vimState; + } + + if ( + vimState.currentMode !== ModeName.Visual && + vimState.currentMode !== ModeName.VisualLine && + vimState.currentMode !== ModeName.EasyMotionMode + ) { + // Normally, you don't have to iterate over all cursors, + // as that is handled for you by the state machine. ESC is + // a special case since runsOnceForEveryCursor is false. + + vimState.allCursors = vimState.allCursors.map(x => x.withNewStop(x.stop.getLeft())); + } + + if (vimState.currentMode === ModeName.SearchInProgressMode) { + if (vimState.globalState.searchState) { + vimState.cursorPosition = vimState.globalState.searchState.searchCursorStartPosition; + } + } + + if (vimState.currentMode === ModeName.Normal && vimState.isMultiCursor) { + vimState.isMultiCursor = false; + } + + if (vimState.currentMode === ModeName.EasyMotionMode) { + // Escape or other termination keys were pressed, exit mode + vimState.easyMotion.clearDecorations(); + vimState.currentMode = ModeName.Normal; + } + + // Abort surround operation + if (vimState.currentMode === ModeName.SurroundInputMode) { + vimState.surround = undefined; + } + + vimState.currentMode = ModeName.Normal; + + if (!vimState.isMultiCursor) { + vimState.allCursors = [vimState.allCursors[0]]; + } + + return vimState; + } +} + +@RegisterAction +class CommandEscReplaceMode extends BaseCommand { + modes = [ModeName.Replace]; + keys = [[''], ['']]; + + public async exec(position: Position, vimState: VimState): Promise { + const timesToRepeat = vimState.replaceState!.timesToRepeat; + let textToAdd = ''; + + for (let i = 1; i < timesToRepeat; i++) { + textToAdd += vimState.replaceState!.newChars.join(''); + } + + vimState.recordedState.transformations.push({ + type: 'insertText', + text: textToAdd, + position: position, + diff: new PositionDiff(0, -1), + }); + + vimState.currentMode = ModeName.Normal; + + return vimState; + } +} + +abstract class CommandEditorScroll extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + runsOnceForEachCountPrefix = false; + keys: string[]; + to: EditorScrollDirection; + by: EditorScrollByUnit; + + public async exec(position: Position, vimState: VimState): Promise { + let timesToRepeat = vimState.recordedState.count || 1; + + vimState.postponedCodeViewChanges.push({ + command: 'editorScroll', + args: { + to: this.to, + by: this.by, + value: timesToRepeat, + revealCursor: true, + select: + [ModeName.Visual, ModeName.VisualBlock, ModeName.VisualLine].indexOf( + vimState.currentMode + ) >= 0, + }, + }); + return vimState; + } +} + +@RegisterAction +class CommandCtrlE extends CommandEditorScroll { + keys = ['']; + to: EditorScrollDirection = 'down'; + by: EditorScrollByUnit = 'line'; +} + +@RegisterAction +class CommandCtrlY extends CommandEditorScroll { + keys = ['']; + to: EditorScrollDirection = 'up'; + by: EditorScrollByUnit = 'line'; +} + +@RegisterAction +class CommandMoveFullPageUp extends CommandEditorScroll { + keys = ['']; + to: EditorScrollDirection = 'up'; + by: EditorScrollByUnit = 'page'; +} + +@RegisterAction +class CommandMoveFullPageDown extends CommandEditorScroll { + keys = ['']; + to: EditorScrollDirection = 'down'; + by: EditorScrollByUnit = 'page'; +} + +@RegisterAction +class CommandMoveHalfPageDown extends CommandEditorScroll { + keys = ['']; + to: EditorScrollDirection = 'down'; + by: EditorScrollByUnit = 'halfPage'; +} + +@RegisterAction +class CommandMoveHalfPageUp extends CommandEditorScroll { + keys = ['']; + to: EditorScrollDirection = 'up'; + by: EditorScrollByUnit = 'halfPage'; +} + +@RegisterAction +export class CommandInsertAtCursor extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['i']; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.currentMode = ModeName.Insert; + return vimState; + } + + public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { + // Only allow this command to be prefixed with a count or nothing, no other + // actions or operators before + let previousActionsNumbers = true; + for (const prevAction of vimState.recordedState.actionsRun) { + if (!(prevAction instanceof CommandNumber)) { + previousActionsNumbers = false; + break; + } + } + + if (vimState.recordedState.actionsRun.length === 0 || previousActionsNumbers) { + return super.couldActionApply(vimState, keysPressed); + } + return false; + } +} + +@RegisterAction +class CommandReplaceAtCursor extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['R']; + runsOnceForEachCountPrefix = false; + + public async exec(position: Position, vimState: VimState): Promise { + let timesToRepeat = vimState.recordedState.count || 1; + + vimState.currentMode = ModeName.Replace; + vimState.replaceState = new ReplaceState(position, timesToRepeat); + + return vimState; + } +} + +@RegisterAction +class CommandReplaceInReplaceMode extends BaseCommand { + modes = [ModeName.Replace]; + keys = ['']; + canBeRepeatedWithDot = true; + + public async exec(position: Position, vimState: VimState): Promise { + const char = this.keysPressed[0]; + const replaceState = vimState.replaceState!; + + if (char === '') { + if (position.isBeforeOrEqual(replaceState.replaceCursorStartPosition)) { + // If you backspace before the beginning of where you started to replace, + // just move the cursor back. + + vimState.cursorPosition = position.getLeft(); + vimState.cursorStartPosition = position.getLeft(); + } else if ( + position.line > replaceState.replaceCursorStartPosition.line || + position.character > replaceState.originalChars.length + ) { + vimState.recordedState.transformations.push({ + type: 'deleteText', + position: position, + }); + } else { + vimState.recordedState.transformations.push({ + type: 'replaceText', + text: replaceState.originalChars[position.character - 1], + start: position.getLeft(), + end: position, + diff: new PositionDiff(0, -1), + }); + } + + replaceState.newChars.pop(); + } else { + if (!position.isLineEnd() && char !== '\n') { + vimState.recordedState.transformations.push({ + type: 'replaceText', + text: char, + start: position, + end: position.getRight(), + diff: new PositionDiff(0, 1), + }); + } else { + vimState.recordedState.transformations.push({ + type: 'insertText', + text: char, + position: position, + }); + } + + replaceState.newChars.push(char); + } + + vimState.currentMode = ModeName.Replace; + return vimState; + } +} + +@RegisterAction +class CommandInsertInSearchMode extends BaseCommand { + modes = [ModeName.SearchInProgressMode]; + keys = [[''], [''], ['']]; + runsOnceForEveryCursor() { + return this.keysPressed[0] === '\n'; + } + + public async exec(position: Position, vimState: VimState): Promise { + const key = this.keysPressed[0]; + const searchState = vimState.globalState.searchState!; + const prevSearchList = vimState.globalState.searchStatePrevious!; + + // handle special keys first + if (key === '' || key === '') { + searchState.searchString = searchState.searchString.slice(0, -1); + } else if (key === '\n') { + vimState.currentMode = vimState.globalState.searchState!.previousMode; + + // Repeat the previous search if no new string is entered + if (searchState.searchString === '') { + if (prevSearchList.length > 0) { + searchState.searchString = prevSearchList[prevSearchList.length - 1].searchString; + } + } + + // Store this search if different than previous + if (vimState.globalState.searchStatePrevious.length !== 0) { + let previousSearchState = vimState.globalState.searchStatePrevious; + if ( + searchState.searchString !== + previousSearchState[previousSearchState.length - 1]!.searchString + ) { + previousSearchState.push(searchState); + } + } else { + vimState.globalState.searchStatePrevious.push(searchState); + } + + // Make sure search history does not exceed configuration option + if (vimState.globalState.searchStatePrevious.length > Configuration.history) { + vimState.globalState.searchStatePrevious.splice(0, 1); + } + + // Update the index to the end of the search history + vimState.globalState.searchStateIndex = vimState.globalState.searchStatePrevious.length - 1; + + // Move cursor to next match + vimState.cursorPosition = searchState.getNextSearchMatchPosition(vimState.cursorPosition).pos; + + return vimState; + } else if (key === '') { + vimState.globalState.searchStateIndex -= 1; + + // Clamp the history index to stay within bounds of search history length + vimState.globalState.searchStateIndex = Math.max(vimState.globalState.searchStateIndex, 0); + + if (prevSearchList[vimState.globalState.searchStateIndex] !== undefined) { + searchState.searchString = + prevSearchList[vimState.globalState.searchStateIndex].searchString; + } + } else if (key === '') { + vimState.globalState.searchStateIndex += 1; + + // If past the first history item, allow user to enter their own search string (not using history) + if ( + vimState.globalState.searchStateIndex > + vimState.globalState.searchStatePrevious.length - 1 + ) { + searchState.searchString = ''; + vimState.globalState.searchStateIndex = vimState.globalState.searchStatePrevious.length; + return vimState; + } + + if (prevSearchList[vimState.globalState.searchStateIndex] !== undefined) { + searchState.searchString = + prevSearchList[vimState.globalState.searchStateIndex].searchString; + } + } else { + searchState.searchString += this.keysPressed[0]; + } + + return vimState; + } +} + +@RegisterAction +class CommandEscInSearchMode extends BaseCommand { + modes = [ModeName.SearchInProgressMode]; + keys = ['']; + runsOnceForEveryCursor() { + return this.keysPressed[0] === '\n'; + } + + public async exec(position: Position, vimState: VimState): Promise { + vimState.currentMode = ModeName.Normal; + vimState.globalState.searchState = undefined; + + return vimState; + } +} + +@RegisterAction +class CommandCtrlVInSearchMode extends BaseCommand { + modes = [ModeName.SearchInProgressMode]; + keys = ['']; + runsOnceForEveryCursor() { + return this.keysPressed[0] === '\n'; + } + + public async exec(position: Position, vimState: VimState): Promise { + const searchState = vimState.globalState.searchState!; + const textFromClipboard = util.clipboardPaste(); + + searchState.searchString += textFromClipboard; + return vimState; + } +} + +@RegisterAction +class CommandCmdVInSearchMode extends BaseCommand { + modes = [ModeName.SearchInProgressMode]; + keys = ['']; + runsOnceForEveryCursor() { + return this.keysPressed[0] === '\n'; + } + + public async exec(position: Position, vimState: VimState): Promise { + const searchState = vimState.globalState.searchState!; + const textFromClipboard = util.clipboardPaste(); + + searchState.searchString += textFromClipboard; + return vimState; + } +} + +/** + * Our Vim implementation selects up to but not including the final character + * of a visual selection, instead opting to render a block cursor on the final + * character. This works for everything except VSCode's native copy command, + * which loses the final character because it's not selected. We override that + * copy command here by default to include the final character. + */ +@RegisterAction +class CommandOverrideCopy extends BaseCommand { + modes = [ + ModeName.Visual, + ModeName.VisualLine, + ModeName.VisualBlock, + ModeName.Insert, + ModeName.Normal, + ]; + keys = ['']; // A special key - see ModeHandler + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + let text = ''; + + if (vimState.currentMode === ModeName.Visual || vimState.currentMode === ModeName.Normal) { + text = vimState.allCursors + .map(range => { + const start = Position.EarlierOf(range.start, range.stop); + const stop = Position.LaterOf(range.start, range.stop); + return vimState.editor.document.getText(new vscode.Range(start, stop.getRight())); + }) + .join('\n'); + } else if (vimState.currentMode === ModeName.VisualLine) { + text = vimState.allCursors + .map(range => { + return vimState.editor.document.getText( + new vscode.Range( + Position.EarlierOf(range.start.getLineBegin(), range.stop.getLineBegin()), + Position.LaterOf(range.start.getLineEnd(), range.stop.getLineEnd()) + ) + ); + }) + .join('\n'); + } else if (vimState.currentMode === ModeName.VisualBlock) { + for (const { line } of Position.IterateLine(vimState)) { + text += line + '\n'; + } + } else if (vimState.currentMode === ModeName.Insert) { + text = vimState.editor.selections + .map(selection => { + return vimState.editor.document.getText(new vscode.Range(selection.start, selection.end)); + }) + .join('\n'); + } + + util.clipboardCopy(text); + // all vim yank operations return to normal mode. + vimState.currentMode = ModeName.Normal; + + return vimState; + } +} + +@RegisterAction +class CommandCmdA extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + keys = ['']; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.cursorStartPosition = new Position(0, vimState.desiredColumn); + vimState.cursorPosition = new Position(TextEditor.getLineCount() - 1, vimState.desiredColumn); + vimState.currentMode = ModeName.VisualLine; + + return vimState; + } +} + +function searchCurrentWord( + position: Position, + vimState: VimState, + direction: SearchDirection, + isExact: boolean +) { + const currentWord = TextEditor.getWord(position); + + // If the search is going left then use `getWordLeft()` on position to start + // at the beginning of the word. This ensures that any matches happen + // outside of the currently selected word. + const searchStartCursorPosition = + direction === SearchDirection.Backward + ? vimState.cursorPosition.getWordLeft(true) + : vimState.cursorPosition; + + return createSearchStateAndMoveToMatch({ + needle: currentWord, + vimState, + direction, + isExact, + searchStartCursorPosition, + }); +} + +function searchCurrentSelection(vimState: VimState, direction: SearchDirection) { + const selection = TextEditor.getSelection(); + const end = new Position(selection.end.line, selection.end.character); + const currentSelection = TextEditor.getText(selection.with(selection.start, end)); + + // Go back to Normal mode, otherwise the selection grows to the next match. + vimState.currentMode = ModeName.Normal; + + // If the search is going left then use `getLeft()` on the selection start. + // If going right then use `getRight()` on the selection end. This ensures + // that any matches happen outside of the currently selected word. + const searchStartCursorPosition = + direction === SearchDirection.Backward + ? vimState.lastVisualSelectionStart.getLeft() + : vimState.lastVisualSelectionEnd.getRight(); + + return createSearchStateAndMoveToMatch({ + needle: currentSelection, + vimState, + direction, + isExact: false, + searchStartCursorPosition, + }); +} + +function createSearchStateAndMoveToMatch(args: { + needle?: string | undefined; + vimState: VimState; + direction: SearchDirection; + isExact: boolean; + searchStartCursorPosition: Position; +}) { + const { needle, vimState, isExact } = args; + + if (needle === undefined || needle.length === 0) { + return vimState; + } + + const searchString = isExact ? `\\b${needle}\\b` : needle; + + // Start a search for the given term. + vimState.globalState.searchState = new SearchState( + args.direction, + vimState.cursorPosition, + searchString, + { isRegex: isExact }, + vimState.currentMode + ); + + vimState.cursorPosition = vimState.globalState.searchState.getNextSearchMatchPosition( + args.searchStartCursorPosition + ).pos; + + // Turn one of the highlighting flags back on (turned off with :nohl) + vimState.globalState.hl = true; + + return vimState; +} + +@RegisterAction +class CommandSearchCurrentWordExactForward extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['*']; + isMotion = true; + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + return searchCurrentWord(position, vimState, SearchDirection.Forward, true); + } +} + +@RegisterAction +class CommandSearchCurrentWordForward extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ['g', '*']; + isMotion = true; + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + return searchCurrentWord(position, vimState, SearchDirection.Forward, false); + } +} + +@RegisterAction +class CommandSearchVisualForward extends BaseCommand { + modes = [ModeName.Visual, ModeName.VisualLine]; + keys = ['*']; + isMotion = true; + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + return searchCurrentSelection(vimState, SearchDirection.Forward); + } +} + +@RegisterAction +class CommandSearchCurrentWordExactBackward extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['#']; + isMotion = true; + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + return searchCurrentWord(position, vimState, SearchDirection.Backward, true); + } +} + +@RegisterAction +class CommandSearchCurrentWordBackward extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ['g', '#']; + isMotion = true; + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + return searchCurrentWord(position, vimState, SearchDirection.Backward, false); + } +} + +@RegisterAction +class CommandSearchVisualBackward extends BaseCommand { + modes = [ModeName.Visual, ModeName.VisualLine]; + keys = ['#']; + isMotion = true; + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + return searchCurrentSelection(vimState, SearchDirection.Backward); + } +} + +@RegisterAction +export class CommandSearchForwards extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + keys = ['/']; + isMotion = true; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.globalState.searchState = new SearchState( + SearchDirection.Forward, + vimState.cursorPosition, + '', + { isRegex: true }, + vimState.currentMode + ); + vimState.currentMode = ModeName.SearchInProgressMode; + + // Reset search history index + vimState.globalState.searchStateIndex = vimState.globalState.searchStatePrevious.length; + + vimState.globalState.hl = true; + + return vimState; + } +} + +@RegisterAction +export class CommandSearchBackwards extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + keys = ['?']; + isMotion = true; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.globalState.searchState = new SearchState( + SearchDirection.Backward, + vimState.cursorPosition, + '', + { isRegex: true }, + vimState.currentMode + ); + vimState.currentMode = ModeName.SearchInProgressMode; + + // Reset search history index + vimState.globalState.searchStateIndex = vimState.globalState.searchStatePrevious.length; + + vimState.globalState.hl = true; + + return vimState; + } +} + +@RegisterAction +export class MarkCommand extends BaseCommand { + keys = ['m', '']; + modes = [ModeName.Normal]; + + public async exec(position: Position, vimState: VimState): Promise { + const markName = this.keysPressed[1]; + + vimState.historyTracker.addMark(position, markName); + + return vimState; + } +} + +@RegisterAction +export class PutCommand extends BaseCommand { + keys = ['p']; + modes = [ModeName.Normal]; + runsOnceForEachCountPrefix = true; + canBeRepeatedWithDot = true; + + constructor(multicursorIndex?: number) { + super(); + this.multicursorIndex = multicursorIndex; + } + public static async GetText( + vimState: VimState, + multicursorIndex: number | undefined = undefined + ): Promise { + const register = await Register.get(vimState); + + if (vimState.isMultiCursor) { + if (multicursorIndex === undefined) { + console.log('ERROR: no multi cursor index when calling PutCommand#getText'); + + throw new Error('Bad!'); + } + + if (vimState.isMultiCursor && typeof register.text === 'object') { + return register.text[multicursorIndex]; + } + } + + return register.text as string; + } + + public async exec( + position: Position, + vimState: VimState, + after: boolean = false, + adjustIndent: boolean = false + ): Promise { + const register = await Register.get(vimState); + const dest = after ? position : position.getRight(); + + if (register.text instanceof RecordedState) { + /** + * Paste content from recordedState. This one is actually complex as + * Vim has internal key code for key strokes.For example, Backspace + * is stored as `<80>kb`. So if you replay a macro, which is stored + * in a register as `a1<80>kb2`, youshall just get `2` inserted as + * `a` represents entering Insert Mode, `<80>bk` represents + * Backspace. However here, we shall + * insert the plain text content of the register, which is `a1<80>kb2`. + */ + vimState.recordedState.transformations.push({ + type: 'macro', + register: vimState.recordedState.registerName, + replay: 'keystrokes', + }); + return vimState; + } else if (typeof register.text === 'object' && vimState.currentMode === ModeName.VisualBlock) { + return await this.execVisualBlockPaste(register.text, position, vimState, after); + } + + let text = await PutCommand.GetText(vimState, this.multicursorIndex); + + let textToAdd: string; + let whereToAddText: Position; + let diff = new PositionDiff(0, 0); + + if (register.registerMode === RegisterMode.CharacterWise) { + textToAdd = text; + whereToAddText = dest; + } else if ( + vimState.currentMode === ModeName.Visual && + register.registerMode === RegisterMode.LineWise + ) { + // in the specific case of linewise register data during visual mode, + // we need extra newline feeds + textToAdd = '\n' + text + '\n'; + whereToAddText = dest; + } else { + if (adjustIndent) { + // Adjust indent to current line + let indentationWidth = TextEditor.getIndentationLevel(TextEditor.getLineAt(position).text); + let firstLineIdentationWidth = TextEditor.getIndentationLevel(text.split('\n')[0]); + + text = text + .split('\n') + .map(line => { + let currentIdentationWidth = TextEditor.getIndentationLevel(line); + let newIndentationWidth = + currentIdentationWidth - firstLineIdentationWidth + indentationWidth; + + return TextEditor.setIndentationLevel(line, newIndentationWidth); + }) + .join('\n'); + } + + if (after) { + // P insert before current line + textToAdd = text + '\n'; + whereToAddText = dest.getLineBegin(); + } else { + // p paste after current line + textToAdd = '\n' + text; + whereToAddText = dest.getLineEnd(); + } + } + + // More vim weirdness: If the thing you're pasting has a newline, the cursor + // stays in the same place. Otherwise, it moves to the end of what you pasted. + + const numNewlines = text.split('\n').length - 1; + const currentLineLength = TextEditor.getLineAt(position).text.length; + + if (register.registerMode === RegisterMode.LineWise) { + const check = text.match(/^\s*/); + let numWhitespace = 0; + + if (check) { + numWhitespace = check[0].length; + } + + if (after) { + diff = PositionDiff.NewBOLDiff(-numNewlines - 1, numWhitespace); + } else { + diff = PositionDiff.NewBOLDiff(currentLineLength > 0 ? 1 : -numNewlines, numWhitespace); + } + } else { + if (text.indexOf('\n') === -1) { + if (!position.isLineEnd()) { + if (after) { + diff = new PositionDiff(0, -1); + } else { + diff = new PositionDiff(0, textToAdd.length); + } + } + } else { + if (position.isLineEnd()) { + diff = PositionDiff.NewBOLDiff(-numNewlines, position.character); + } else { + if (after) { + diff = PositionDiff.NewBOLDiff(-numNewlines, position.character); + } else { + diff = new PositionDiff(0, 1); + } + } + } + } + + vimState.recordedState.transformations.push({ + type: 'insertText', + text: textToAdd, + position: whereToAddText, + diff: diff, + }); + + vimState.currentRegisterMode = register.registerMode; + return vimState; + } + + private async execVisualBlockPaste( + block: string[], + position: Position, + vimState: VimState, + after: boolean + ): Promise { + if (after) { + position = position.getRight(); + } + + // Add empty lines at the end of the document, if necessary. + let linesToAdd = Math.max(0, block.length - (TextEditor.getLineCount() - position.line) + 1); + + if (linesToAdd > 0) { + await TextEditor.insertAt( + Array(linesToAdd).join('\n'), + new Position( + TextEditor.getLineCount() - 1, + TextEditor.getLineAt(new Position(TextEditor.getLineCount() - 1, 0)).text.length + ) + ); + } + + // paste the entire block. + for (let lineIndex = position.line; lineIndex < position.line + block.length; lineIndex++) { + const line = block[lineIndex - position.line]; + const insertPos = new Position( + lineIndex, + Math.min(position.character, TextEditor.getLineAt(new Position(lineIndex, 0)).text.length) + ); + + await TextEditor.insertAt(line, insertPos); + } + + vimState.currentRegisterMode = RegisterMode.FigureItOutFromCurrentMode; + return vimState; + } + + public async execCount(position: Position, vimState: VimState): Promise { + const result = await super.execCount(position, vimState); + + if ( + vimState.effectiveRegisterMode() === RegisterMode.LineWise && + vimState.recordedState.count > 0 + ) { + const numNewlines = + (await PutCommand.GetText(vimState, this.multicursorIndex)).split('\n').length * + vimState.recordedState.count; + + result.recordedState.transformations.push({ + type: 'moveCursor', + diff: new PositionDiff(-numNewlines + 1, 0), + cursorIndex: this.multicursorIndex, + }); + } + + return result; + } +} + +@RegisterAction +export class GPutCommand extends BaseCommand { + keys = ['g', 'p']; + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + runsOnceForEachCountPrefix = true; + canBeRepeatedWithDot = true; + + public async exec(position: Position, vimState: VimState): Promise { + const result = await new PutCommand().exec(position, vimState); + + return result; + } + + public async execCount(position: Position, vimState: VimState): Promise { + const register = await Register.get(vimState); + let addedLinesCount: number; + + if (register.text instanceof RecordedState) { + vimState.recordedState.transformations.push({ + type: 'macro', + register: vimState.recordedState.registerName, + replay: 'keystrokes', + }); + + return vimState; + } + if (typeof register.text === 'object') { + // visual block mode + addedLinesCount = register.text.length * vimState.recordedState.count; + } else { + addedLinesCount = register.text.split('\n').length; + } + + const result = await super.execCount(position, vimState); + + if (vimState.effectiveRegisterMode() === RegisterMode.LineWise) { + const line = TextEditor.getLineAt(position).text; + const addAnotherLine = line.length > 0 && addedLinesCount > 1; + + result.recordedState.transformations.push({ + type: 'moveCursor', + diff: PositionDiff.NewBOLDiff(1 + (addAnotherLine ? 1 : 0), 0), + cursorIndex: this.multicursorIndex, + }); + } + + return result; + } +} + +@RegisterAction +export class PutWithIndentCommand extends BaseCommand { + keys = [']', 'p']; + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + runsOnceForEachCountPrefix = true; + canBeRepeatedWithDot = true; + + public async exec(position: Position, vimState: VimState): Promise { + const result = await new PutCommand().exec(position, vimState, false, true); + return result; + } + + public async execCount(position: Position, vimState: VimState): Promise { + return await super.execCount(position, vimState); + } +} + +@RegisterAction +export class PutCommandVisual extends BaseCommand { + keys = [['p'], ['P']]; + modes = [ModeName.Visual, ModeName.VisualLine]; + runsOnceForEachCountPrefix = true; + + public async exec( + position: Position, + vimState: VimState, + after: boolean = false + ): Promise { + let start = vimState.cursorStartPosition; + let end = vimState.cursorPosition; + const isLineWise = vimState.currentMode === ModeName.VisualLine; + if (start.isAfter(end)) { + [start, end] = [end, start]; + } + + // If the to be inserted text is linewise we have a seperate logic delete the + // selection first than insert + let register = await Register.get(vimState); + if (register.registerMode === RegisterMode.LineWise) { + let deleteResult = await new operator.DeleteOperator(this.multicursorIndex).run( + vimState, + start, + end, + false + ); + // to ensure, that the put command nows this is + // an linewise register insertion in visual mode + let oldMode = deleteResult.currentMode; + deleteResult.currentMode = ModeName.Visual; + deleteResult = await new PutCommand().exec(start, deleteResult, true); + deleteResult.currentMode = oldMode; + return deleteResult; + } + + // The reason we need to handle Delete and Yank separately is because of + // linewise mode. If we're in visualLine mode, then we want to copy + // linewise but not necessarily delete linewise. + let putResult = await new PutCommand(this.multicursorIndex).exec(start, vimState, true); + putResult.currentRegisterMode = isLineWise ? RegisterMode.LineWise : RegisterMode.CharacterWise; + putResult.recordedState.registerName = Configuration.useSystemClipboard ? '*' : '"'; + putResult = await new operator.YankOperator(this.multicursorIndex).run(putResult, start, end); + putResult.currentRegisterMode = RegisterMode.CharacterWise; + putResult = await new operator.DeleteOperator(this.multicursorIndex).run( + putResult, + start, + end.getLeftIfEOL(), + false + ); + putResult.currentRegisterMode = RegisterMode.FigureItOutFromCurrentMode; + return putResult; + } + + // TODO - execWithCount +} + +@RegisterAction +export class PutBeforeCommand extends BaseCommand { + public keys = ['P']; + public modes = [ModeName.Normal]; + canBeRepeatedWithDot = true; + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + const command = new PutCommand(); + command.multicursorIndex = this.multicursorIndex; + + const result = await command.exec(position, vimState, true); + + return result; + } +} + +@RegisterAction +export class GPutBeforeCommand extends BaseCommand { + keys = ['g', 'P']; + modes = [ModeName.Normal]; + + public async exec(position: Position, vimState: VimState): Promise { + const result = await new PutCommand().exec(position, vimState, true); + const register = await Register.get(vimState); + let addedLinesCount: number; + + if (register.text instanceof RecordedState) { + vimState.recordedState.transformations.push({ + type: 'macro', + register: vimState.recordedState.registerName, + replay: 'keystrokes', + }); + + return vimState; + } else if (typeof register.text === 'object') { + // visual block mode + addedLinesCount = register.text.length * vimState.recordedState.count; + } else { + addedLinesCount = register.text.split('\n').length; + } + + if (vimState.effectiveRegisterMode() === RegisterMode.LineWise) { + const line = TextEditor.getLineAt(position).text; + const addAnotherLine = line.length > 0 && addedLinesCount > 1; + + result.recordedState.transformations.push({ + type: 'moveCursor', + diff: PositionDiff.NewBOLDiff(1 + (addAnotherLine ? 1 : 0), 0), + cursorIndex: this.multicursorIndex, + }); + } + + return result; + } +} + +@RegisterAction +export class PutBeforeWithIndentCommand extends BaseCommand { + keys = ['[', 'p']; + modes = [ModeName.Normal]; + + public async exec(position: Position, vimState: VimState): Promise { + const result = await new PutCommand().exec(position, vimState, true, true); + + if (vimState.effectiveRegisterMode() === RegisterMode.LineWise) { + result.cursorPosition = result.cursorPosition + .getPreviousLineBegin() + .getFirstLineNonBlankChar(); + } + + return result; + } +} + +@RegisterAction +class CommandShowCommandLine extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + keys = [':']; + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + vimState.recordedState.transformations.push({ + type: 'showCommandLine', + }); + + if (vimState.currentMode === ModeName.Normal) { + vimState.commandInitialText = ''; + } else { + vimState.commandInitialText = "'<,'>"; + } + vimState.currentMode = ModeName.Normal; + + return vimState; + } +} + +@RegisterAction +class CommandDot extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['.']; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.recordedState.transformations.push({ + type: 'dot', + }); + + return vimState; + } +} + +abstract class CommandFold extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + commandName: string; + + public async exec(position: Position, vimState: VimState): Promise { + await vscode.commands.executeCommand(this.commandName); + vimState.currentMode = ModeName.Normal; + return vimState; + } +} + +@RegisterAction +class CommandCloseFold extends CommandFold { + keys = ['z', 'c']; + commandName = 'editor.fold'; + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + let timesToRepeat = vimState.recordedState.count || 1; + await vscode.commands.executeCommand('editor.fold', { levels: timesToRepeat, direction: 'up' }); + vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); + return vimState; + } +} + +@RegisterAction +class CommandCloseAllFolds extends CommandFold { + keys = ['z', 'M']; + commandName = 'editor.foldAll'; +} + +@RegisterAction +class CommandOpenFold extends CommandFold { + keys = ['z', 'o']; + commandName = 'editor.unfold'; + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + let timesToRepeat = vimState.recordedState.count || 1; + await vscode.commands.executeCommand('editor.unfold', { + levels: timesToRepeat, + direction: 'up', + }); + + return vimState; + } +} + +@RegisterAction +class CommandOpenAllFolds extends CommandFold { + keys = ['z', 'R']; + commandName = 'editor.unfoldAll'; +} + +@RegisterAction +class CommandCloseAllFoldsRecursively extends CommandFold { + modes = [ModeName.Normal]; + keys = ['z', 'C']; + commandName = 'editor.foldRecursively'; +} + +@RegisterAction +class CommandOpenAllFoldsRecursively extends CommandFold { + modes = [ModeName.Normal]; + keys = ['z', 'O']; + commandName = 'editor.unfoldRecursively'; +} + +@RegisterAction +class CommandCenterScroll extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + keys = ['z', 'z']; + + public async exec(position: Position, vimState: VimState): Promise { + // In these modes you want to center on the cursor position + vimState.editor.revealRange( + new vscode.Range(vimState.cursorPosition, vimState.cursorPosition), + vscode.TextEditorRevealType.InCenter + ); + + return vimState; + } +} + +@RegisterAction +class CommandCenterScrollFirstChar extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + keys = ['z', '.']; + + public async exec(position: Position, vimState: VimState): Promise { + // In these modes you want to center on the cursor position + // This particular one moves cursor to first non blank char though + vimState.editor.revealRange( + new vscode.Range(vimState.cursorPosition, vimState.cursorPosition), + vscode.TextEditorRevealType.InCenter + ); + + // Move cursor to first char of line + vimState.cursorPosition = vimState.cursorPosition.getFirstLineNonBlankChar(); + + return vimState; + } +} + +@RegisterAction +class CommandTopScroll extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['z', 't']; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.postponedCodeViewChanges.push({ + command: 'revealLine', + args: { + lineNumber: position.line, + at: 'top', + }, + }); + return vimState; + } +} + +@RegisterAction +class CommandTopScrollFirstChar extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + keys = ['z', '\n']; + + public async exec(position: Position, vimState: VimState): Promise { + // In these modes you want to center on the cursor position + // This particular one moves cursor to first non blank char though + vimState.postponedCodeViewChanges.push({ + command: 'revealLine', + args: { + lineNumber: position.line, + at: 'top', + }, + }); + + // Move cursor to first char of line + vimState.cursorPosition = vimState.cursorPosition.getFirstLineNonBlankChar(); + + return vimState; + } +} + +@RegisterAction +class CommandBottomScroll extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['z', 'b']; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.postponedCodeViewChanges.push({ + command: 'revealLine', + args: { + lineNumber: position.line, + at: 'bottom', + }, + }); + return vimState; + } +} + +@RegisterAction +class CommandBottomScrollFirstChar extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + keys = ['z', '-']; + + public async exec(position: Position, vimState: VimState): Promise { + // In these modes you want to center on the cursor position + // This particular one moves cursor to first non blank char though + vimState.postponedCodeViewChanges.push({ + command: 'revealLine', + args: { + lineNumber: position.line, + at: 'bottom', + }, + }); + + // Move cursor to first char of line + vimState.cursorPosition = vimState.cursorPosition.getFirstLineNonBlankChar(); + + return vimState; + } +} + +@RegisterAction +class CommandGoToOtherEndOfHighlightedText extends BaseCommand { + modes = [ModeName.Visual, ModeName.VisualLine]; + keys = ['o']; + + public async exec(position: Position, vimState: VimState): Promise { + [vimState.cursorStartPosition, vimState.cursorPosition] = [ + vimState.cursorPosition, + vimState.cursorStartPosition, + ]; + + return vimState; + } +} + +@RegisterAction +class CommandUndo extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['u']; + runsOnceForEveryCursor() { + return false; + } + mustBeFirstKey = true; + + public async exec(position: Position, vimState: VimState): Promise { + const newPositions = await vimState.historyTracker.goBackHistoryStep(); + + if (newPositions !== undefined) { + vimState.allCursors = newPositions.map(x => new Range(x, x)); + } + + vimState.alteredHistory = true; + return vimState; + } +} + +@RegisterAction +class CommandRedo extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['']; + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + const newPositions = await vimState.historyTracker.goForwardHistoryStep(); + + if (newPositions !== undefined) { + vimState.allCursors = newPositions.map(x => new Range(x, x)); + } + + vimState.alteredHistory = true; + return vimState; + } +} + +@RegisterAction +class CommandDeleteToLineEnd extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['D']; + canBeRepeatedWithDot = true; + runsOnceForEveryCursor() { + return true; + } + + public async exec(position: Position, vimState: VimState): Promise { + if (position.isLineEnd()) { + return vimState; + } + + return await new operator.DeleteOperator(this.multicursorIndex).run( + vimState, + position, + position.getLineEnd().getLeft() + ); + } +} + +@RegisterAction +export class CommandYankFullLine extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['Y']; + + public async exec(position: Position, vimState: VimState): Promise { + const linesDown = (vimState.recordedState.count || 1) - 1; + const start = position.getLineBegin(); + const end = new Position(position.line + linesDown, 0).getLineEnd().getLeft(); + + vimState.currentRegisterMode = RegisterMode.LineWise; + + return await new operator.YankOperator().run(vimState, start, end); + } +} + +@RegisterAction +class CommandChangeToLineEnd extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['C']; + runsOnceForEachCountPrefix = false; + mustBeFirstKey = true; + + public async exec(position: Position, vimState: VimState): Promise { + let count = vimState.recordedState.count || 1; + + return new operator.ChangeOperator().run( + vimState, + position, + position + .getDownByCount(Math.max(0, count - 1)) + .getLineEnd() + .getLeft() + ); + } +} + +@RegisterAction +class CommandClearLine extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['S']; + runsOnceForEachCountPrefix = false; + + public async exec(position: Position, vimState: VimState): Promise { + let count = vimState.recordedState.count || 1; + let end = position + .getDownByCount(Math.max(0, count - 1)) + .getLineEnd() + .getLeft(); + return new operator.ChangeOperator().run( + vimState, + position.getLineBeginRespectingIndent(), + end + ); + } +} + +@RegisterAction +class CommandExitVisualMode extends BaseCommand { + modes = [ModeName.Visual, ModeName.VisualLine]; + keys = ['v']; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.currentMode = ModeName.Normal; + + return vimState; + } +} + +@RegisterAction +class CommandVisualMode extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['v']; + isCompleteAction = false; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.currentMode = ModeName.Visual; + + return vimState; + } +} + +@RegisterAction +class CommandReselectVisual extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['g', 'v']; + + public async exec(position: Position, vimState: VimState): Promise { + // Try to restore selection only if valid + if ( + vimState.lastVisualSelectionEnd !== undefined && + vimState.lastVisualSelectionStart !== undefined && + vimState.lastVisualMode !== undefined + ) { + if (vimState.lastVisualSelectionEnd.line <= TextEditor.getLineCount() - 1) { + vimState.currentMode = vimState.lastVisualMode; + vimState.cursorStartPosition = vimState.lastVisualSelectionStart; + vimState.cursorPosition = vimState.lastVisualSelectionEnd.getLeft(); + } + } + return vimState; + } +} + +@RegisterAction +class CommandVisualBlockMode extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualBlock]; + keys = ['']; + + public async exec(position: Position, vimState: VimState): Promise { + if (vimState.currentMode === ModeName.VisualBlock) { + vimState.currentMode = ModeName.Normal; + } else { + vimState.currentMode = ModeName.VisualBlock; + } + + return vimState; + } +} + +@RegisterAction +class CommandVisualLineMode extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual]; + keys = ['V']; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.currentMode = ModeName.VisualLine; + + return vimState; + } +} + +@RegisterAction +class CommandExitVisualLineMode extends BaseCommand { + modes = [ModeName.VisualLine]; + keys = ['V']; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.currentMode = ModeName.Normal; + + return vimState; + } +} + +@RegisterAction +class CommandOpenFile extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual]; + keys = ['g', 'f']; + + public async exec(position: Position, vimState: VimState): Promise { + let fullFilePath: string = ''; + + if (vimState.currentMode === ModeName.Visual) { + const selection = TextEditor.getSelection(); + const end = new Position(selection.end.line, selection.end.character + 1); + fullFilePath = TextEditor.getText(selection.with(selection.start, end)); + } else { + const start = position.getFilePathLeft(true); + const end = position.getFilePathRight(); + const range = new vscode.Range(start, end); + + fullFilePath = TextEditor.getText(range).trim(); + } + const fileInfo = fullFilePath.match(/(.*?(?=:[0-9]+)|.*):?([0-9]*)$/); + if (fileInfo) { + const filePath = fileInfo[1]; + const lineNumber = parseInt(fileInfo[2], 10); + const fileCommand = new FileCommand({ name: filePath, lineNumber: lineNumber }); + fileCommand.execute(); + } + + return vimState; + } +} + +@RegisterAction +class CommandGoToDefinition extends BaseCommand { + modes = [ModeName.Normal]; + keys = [['g', 'd'], ['']]; + + public async exec(position: Position, vimState: VimState): Promise { + const oldActiveEditor = vimState.editor; + + await vscode.commands.executeCommand('editor.action.goToDeclaration'); + + if (oldActiveEditor === vimState.editor) { + vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start); + } + + return vimState; + } +} + +@RegisterAction +class CommandGoBackInChangelist extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['g', ';']; + + public async exec(position: Position, vimState: VimState): Promise { + const originalIndex = vimState.historyTracker.changelistIndex; + const prevPos = vimState.historyTracker.getChangePositionAtindex(originalIndex - 1); + const currPos = vimState.historyTracker.getChangePositionAtindex(originalIndex); + + if (prevPos !== undefined) { + vimState.cursorPosition = prevPos[0]; + vimState.historyTracker.changelistIndex = originalIndex - 1; + } else if (currPos !== undefined) { + vimState.cursorPosition = currPos[0]; + } + + return vimState; + } +} + +@RegisterAction +class CommandGoForwardInChangelist extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['g', ',']; + + public async exec(position: Position, vimState: VimState): Promise { + const originalIndex = vimState.historyTracker.changelistIndex; + const nextPos = vimState.historyTracker.getChangePositionAtindex(originalIndex + 1); + const currPos = vimState.historyTracker.getChangePositionAtindex(originalIndex); + + if (nextPos !== undefined) { + vimState.cursorPosition = nextPos[0]; + vimState.historyTracker.changelistIndex = originalIndex + 1; + } else if (currPos !== undefined) { + vimState.cursorPosition = currPos[0]; + } + + return vimState; + } +} + +@RegisterAction +class CommandGoLastChange extends BaseCommand { + modes = [ModeName.Normal]; + keys = [['`', '.'], ["'", '.']]; + + public async exec(position: Position, vimState: VimState): Promise { + const lastPos = vimState.historyTracker.getLastHistoryStartPosition(); + + if (lastPos !== undefined) { + vimState.cursorPosition = lastPos[0]; + } + + return vimState; + } +} + +@RegisterAction +class CommandInsertAtLastChange extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['g', 'i']; + + public async exec(position: Position, vimState: VimState): Promise { + const lastPos = vimState.historyTracker.getLastChangeEndPosition(); + + if (lastPos !== undefined) { + vimState.cursorPosition = lastPos; + vimState.currentMode = ModeName.Insert; + } + + return vimState; + } +} + +@RegisterAction +export class CommandInsertAtFirstCharacter extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual]; + keys = ['I']; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.currentMode = ModeName.Insert; + vimState.cursorPosition = position.getFirstLineNonBlankChar(); + + return vimState; + } +} + +@RegisterAction +class CommandInsertAtLineBegin extends BaseCommand { + modes = [ModeName.Normal]; + mustBeFirstKey = true; + keys = ['g', 'I']; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.currentMode = ModeName.Insert; + vimState.cursorPosition = position.getLineBegin(); + + return vimState; + } +} + +@RegisterAction +export class CommandInsertAfterCursor extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['a']; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.currentMode = ModeName.Insert; + vimState.cursorPosition = position.getRight(); + + return vimState; + } + + public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { + // Only allow this command to be prefixed with a count or nothing, no other + // actions or operators before + let previousActionsNumbers = true; + for (const prevAction of vimState.recordedState.actionsRun) { + if (!(prevAction instanceof CommandNumber)) { + previousActionsNumbers = false; + break; + } + } + + if (vimState.recordedState.actionsRun.length === 0 || previousActionsNumbers) { + return super.couldActionApply(vimState, keysPressed); + } + return false; + } +} + +@RegisterAction +export class CommandInsertAtLineEnd extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual]; + keys = ['A']; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.currentMode = ModeName.Insert; + vimState.cursorPosition = position.getLineEnd(); + + return vimState; + } +} + +@RegisterAction +class CommandInsertNewLineAbove extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['O']; + runsOnceForEveryCursor() { + return false; + } + + public async execCount(position: Position, vimState: VimState): Promise { + vimState.currentMode = ModeName.Insert; + let count = vimState.recordedState.count || 1; + // Why do we do this? Who fucking knows??? If the cursor is at the + // beginning of the line, then editor.action.insertLineBefore does some + // weird things with following paste command. Refer to + // https://github.com/VSCodeVim/Vim/pull/1663#issuecomment-300573129 for + // more details. + const tPos = Position.FromVSCodePosition( + vscode.window.activeTextEditor!.selection.active + ).getRight(); + vscode.window.activeTextEditor!.selection = new vscode.Selection(tPos, tPos); + for (let i = 0; i < count; i++) { + await vscode.commands.executeCommand('editor.action.insertLineBefore'); + } + + vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); + for (let i = 0; i < count; i++) { + const newPos = new Position( + vimState.allCursors[0].start.line + i, + vimState.allCursors[0].start.character + ); + vimState.allCursors.push(new Range(newPos, newPos)); + } + vimState.allCursors = vimState.allCursors.reverse(); + vimState.isFakeMultiCursor = true; + vimState.isMultiCursor = true; + return vimState; + } +} + +@RegisterAction +class CommandInsertNewLineBefore extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['o']; + runsOnceForEveryCursor() { + return false; + } + + public async execCount(position: Position, vimState: VimState): Promise { + vimState.currentMode = ModeName.Insert; + let count = vimState.recordedState.count || 1; + + for (let i = 0; i < count; i++) { + await vscode.commands.executeCommand('editor.action.insertLineAfter'); + } + vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); + for (let i = 1; i < count; i++) { + const newPos = new Position( + vimState.allCursors[0].start.line - i, + vimState.allCursors[0].start.character + ); + vimState.allCursors.push(new Range(newPos, newPos)); + + // Ahhhhhh. We have to manually set cursor position here as we need text + // transformations AND to set multiple cursors. + vimState.recordedState.transformations.push({ + type: 'insertText', + text: TextEditor.setIndentationLevel('', newPos.character), + position: newPos, + cursorIndex: i, + manuallySetCursorPositions: true, + }); + } + vimState.allCursors = vimState.allCursors.reverse(); + vimState.isFakeMultiCursor = true; + vimState.isMultiCursor = true; + return vimState; + } +} + +@RegisterAction +class CommandNavigateBack extends BaseCommand { + modes = [ModeName.Normal]; + keys = [[''], ['']]; + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + const oldActiveEditor = vimState.editor; + + await vscode.commands.executeCommand('workbench.action.navigateBack'); + + if (oldActiveEditor === vimState.editor) { + vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start); + } + + return vimState; + } +} + +@RegisterAction +class CommandNavigateForward extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['']; + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + const oldActiveEditor = vimState.editor; + + await vscode.commands.executeCommand('workbench.action.navigateForward'); + + if (oldActiveEditor === vimState.editor) { + vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start); + } + + return vimState; + } +} + +@RegisterAction +class CommandNavigateLast extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['`', '`']; + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + const oldActiveEditor = vimState.editor; + + await vscode.commands.executeCommand('workbench.action.navigateLast'); + + if (oldActiveEditor === vimState.editor) { + vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start); + } + + return vimState; + } +} + +@RegisterAction +class CommandNavigateLastBOL extends BaseCommand { + modes = [ModeName.Normal]; + keys = ["'", "'"]; + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + const oldActiveEditor = vimState.editor; + + await vscode.commands.executeCommand('workbench.action.navigateLast'); + + if (oldActiveEditor === vimState.editor) { + const pos = Position.FromVSCodePosition(vimState.editor.selection.start); + vimState.cursorPosition = pos.getFirstLineNonBlankChar(); + } + + return vimState; + } +} + +@RegisterAction +class CommandQuit extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['', 'q']; + + public async exec(position: Position, vimState: VimState): Promise { + new QuitCommand({}).execute(); + + return vimState; + } +} + +@RegisterAction +class CommandOnly extends BaseCommand { + modes = [ModeName.Normal]; + keys = [['', 'o'], ['', 'C-o']]; + + public async exec(position: Position, vimState: VimState): Promise { + new OnlyCommand({}).execute(); + + return vimState; + } +} + +@RegisterAction +class MoveToRightPane extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = [['', 'l'], ['', ''], ['']]; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.postponedCodeViewChanges.push({ + command: 'workbench.action.navigateRight', + args: {}, + }); + + return vimState; + } +} + +@RegisterAction +class MoveToLowerPane extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = [['', 'j'], ['', ''], ['']]; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.postponedCodeViewChanges.push({ + command: 'workbench.action.navigateDown', + args: {}, + }); + + return vimState; + } +} + +@RegisterAction +class MoveToUpperPane extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = [['', 'k'], ['', ''], ['']]; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.postponedCodeViewChanges.push({ + command: 'workbench.action.navigateUp', + args: {}, + }); + + return vimState; + } +} + +@RegisterAction +class MoveToLeftPane extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = [['', 'h'], ['', ''], ['']]; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.postponedCodeViewChanges.push({ + command: 'workbench.action.navigateLeft', + args: {}, + }); + + return vimState; + } +} + +@RegisterAction +class CycleThroughPanes extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = [['', ''], ['', 'w']]; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.postponedCodeViewChanges.push({ + command: 'workbench.action.navigateEditorGroups', + args: {}, + }); + + return vimState; + } +} + +class BaseTabCommand extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + runsOnceForEachCountPrefix = true; +} + +@RegisterAction +class VerticalSplit extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ['', 'v']; + + public async exec(position: Position, vimState: VimState): Promise { + vimState.postponedCodeViewChanges.push({ + command: 'workbench.action.splitEditor', + args: {}, + }); + + return vimState; + } +} + +@RegisterAction +class CommandTabNext extends BaseTabCommand { + keys = [['g', 't'], ['']]; + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + new TabCommand({ + tab: Tab.Next, + count: vimState.recordedState.count, + }).execute(); + + return vimState; + } +} + +@RegisterAction +class CommandTabPrevious extends BaseTabCommand { + keys = [['g', 'T'], ['']]; + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + new TabCommand({ + tab: Tab.Previous, + count: 1, + }).execute(); + + return vimState; + } +} + +@RegisterAction +class ActionDeleteChar extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['x']; + runsOnceForEachCountPrefix = true; + canBeRepeatedWithDot = true; + + public async exec(position: Position, vimState: VimState): Promise { + // If line is empty, do nothing + if (TextEditor.getLineAt(position).text.length < 1) { + return vimState; + } + + const state = await new operator.DeleteOperator(this.multicursorIndex).run( + vimState, + position, + position + ); + + state.currentMode = ModeName.Normal; + + return state; + } +} + +@RegisterAction +class ActionDeleteCharWithDeleteKey extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['']; + runsOnceForEachCountPrefix = true; + canBeRepeatedWithDot = true; + + public async execCount(position: Position, vimState: VimState): Promise { + // If has a count in front of it, then deletes a character + // off the count. Therefore, 100x, would apply 'x' 10 times. + // http://vimdoc.sourceforge.net/htmldoc/change.html# + if (vimState.recordedState.count !== 0) { + vimState.recordedState.count = Math.floor(vimState.recordedState.count / 10); + vimState.recordedState.actionKeys = vimState.recordedState.count.toString().split(''); + vimState.recordedState.commandList = vimState.recordedState.count.toString().split(''); + this.isCompleteAction = false; + return vimState; + } + return await new ActionDeleteChar().execCount(position, vimState); + } +} + +@RegisterAction +class ActionDeleteLastChar extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['X']; + canBeRepeatedWithDot = true; + + public async exec(position: Position, vimState: VimState): Promise { + if (position.character === 0) { + return vimState; + } + + return await new operator.DeleteOperator(this.multicursorIndex).run( + vimState, + position.getLeft(), + position.getLeft() + ); + } +} + +@RegisterAction +class ActionJoin extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['J']; + canBeRepeatedWithDot = true; + runsOnceForEachCountPrefix = false; + + private firstNonWhitespaceIndex(str: string): number { + for (let i = 0, len = str.length; i < len; i++) { + let chCode = str.charCodeAt(i); + if (chCode !== 32 /** space */ && chCode !== 9 /** tab */) { + return i; + } + } + return -1; + } + + public async execJoinLines( + startPosition: Position, + position: Position, + vimState: VimState, + count: number + ): Promise { + count = count - 1 || 1; + + let startLineNumber: number, + startColumn: number, + endLineNumber: number, + endColumn: number, + columnDeltaOffset: number = 0; + + if (startPosition.isEqual(position) || startPosition.line === position.line) { + if (position.line + 1 < TextEditor.getLineCount()) { + startLineNumber = position.line; + startColumn = 0; + endLineNumber = startLineNumber + count; + endColumn = TextEditor.getLineMaxColumn(endLineNumber); + } else { + startLineNumber = position.line; + startColumn = 0; + endLineNumber = position.line; + endColumn = TextEditor.getLineMaxColumn(endLineNumber); + } + } else { + startLineNumber = startPosition.line; + startColumn = 0; + endLineNumber = position.line; + endColumn = TextEditor.getLineMaxColumn(endLineNumber); + } + + let trimmedLinesContent = TextEditor.getLineAt(startPosition).text; + + for (let i = startLineNumber + 1; i <= endLineNumber; i++) { + let lineText = TextEditor.getLineAt(new Position(i, 0)).text; + + let firstNonWhitespaceIdx = this.firstNonWhitespaceIndex(lineText); + + if (firstNonWhitespaceIdx >= 0) { + let insertSpace = true; + + if ( + trimmedLinesContent === '' || + trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === ' ' || + trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === '\t' + ) { + insertSpace = false; + } + + let lineTextWithoutIndent = lineText.substr(firstNonWhitespaceIdx); + + if (lineTextWithoutIndent.charAt(0) === ')') { + insertSpace = false; + } + + trimmedLinesContent += (insertSpace ? ' ' : '') + lineTextWithoutIndent; + + if (insertSpace) { + columnDeltaOffset = lineTextWithoutIndent.length + 1; + } else { + columnDeltaOffset = lineTextWithoutIndent.length; + } + } else { + if ( + trimmedLinesContent === '' || + trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === ' ' || + trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === '\t' + ) { + columnDeltaOffset += 0; + } else { + trimmedLinesContent += ' '; + columnDeltaOffset += 1; + } + } + } + + let deleteStartPosition = new Position(startLineNumber, startColumn); + let deleteEndPosition = new Position(endLineNumber, endColumn); + + if (!deleteStartPosition.isEqual(deleteEndPosition)) { + if (startPosition.isEqual(position)) { + vimState.recordedState.transformations.push({ + type: 'replaceText', + text: trimmedLinesContent, + start: deleteStartPosition, + end: deleteEndPosition, + diff: new PositionDiff( + 0, + trimmedLinesContent.length - columnDeltaOffset - position.character + ), + }); + } else { + vimState.recordedState.transformations.push({ + type: 'replaceText', + text: trimmedLinesContent, + start: deleteStartPosition, + end: deleteEndPosition, + manuallySetCursorPositions: true, + }); + + vimState.cursorPosition = new Position( + startPosition.line, + trimmedLinesContent.length - columnDeltaOffset + ); + vimState.cursorStartPosition = vimState.cursorPosition; + vimState.currentMode = ModeName.Normal; + } + } + + return vimState; + } + + public async execCount(position: Position, vimState: VimState): Promise { + let timesToRepeat = vimState.recordedState.count || 1; + let resultingCursors: Range[] = []; + let i = 0; + + const cursorsToIterateOver = vimState.allCursors + .map(x => new Range(x.start, x.stop)) + .sort( + (a, b) => + a.start.line > b.start.line || + (a.start.line === b.start.line && a.start.character > b.start.character) + ? 1 + : -1 + ); + + for (const { start, stop } of cursorsToIterateOver) { + this.multicursorIndex = i++; + + vimState.cursorPosition = stop; + vimState.cursorStartPosition = start; + + vimState = await this.execJoinLines(start, stop, vimState, timesToRepeat); + + resultingCursors.push(new Range(vimState.cursorStartPosition, vimState.cursorPosition)); + + for (const transformation of vimState.recordedState.transformations) { + if (isTextTransformation(transformation) && transformation.cursorIndex === undefined) { + transformation.cursorIndex = this.multicursorIndex; + } + } + } + + vimState.allCursors = resultingCursors; + + return vimState; + } +} + +@RegisterAction +class ActionJoinVisualMode extends BaseCommand { + modes = [ModeName.Visual, ModeName.VisualLine]; + keys = ['J']; + + public async exec(position: Position, vimState: VimState): Promise { + let actionJoin = new ActionJoin(); + let start = Position.FromVSCodePosition(vimState.editor.selection.start); + let end = Position.FromVSCodePosition(vimState.editor.selection.end); + + if (start.isAfter(end)) { + [start, end] = [end, start]; + } + + /** + * For joining lines, Visual Line behaves the same as Visual so we align the register mode here. + */ + vimState.currentRegisterMode = RegisterMode.CharacterWise; + vimState = await actionJoin.execJoinLines(start, end, vimState, 1); + + return vimState; + } +} + +@RegisterAction +class ActionJoinNoWhitespace extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['g', 'J']; + canBeRepeatedWithDot = true; + runsOnceForEachCountPrefix = true; + + // gJ is essentially J without the edge cases. ;-) + + public async exec(position: Position, vimState: VimState): Promise { + if (position.line === TextEditor.getLineCount() - 1) { + return vimState; // TODO: bell + } + + let lineOne = TextEditor.getLineAt(position).text; + let lineTwo = TextEditor.getLineAt(position.getNextLineBegin()).text; + + lineTwo = lineTwo.substring(position.getNextLineBegin().getFirstLineNonBlankChar().character); + + let resultLine = lineOne + lineTwo; + + let newState = await new operator.DeleteOperator(this.multicursorIndex).run( + vimState, + position.getLineBegin(), + lineTwo.length > 0 + ? position + .getNextLineBegin() + .getLineEnd() + .getLeft() + : position.getLineEnd() + ); + + vimState.recordedState.transformations.push({ + type: 'insertText', + text: resultLine, + position: position, + diff: new PositionDiff(0, -lineTwo.length), + }); + + newState.cursorPosition = new Position(position.line, lineOne.length); + + return newState; + } +} + +@RegisterAction +class ActionJoinNoWhitespaceVisualMode extends BaseCommand { + modes = [ModeName.Visual]; + keys = ['g', 'J']; + + public async exec(position: Position, vimState: VimState): Promise { + let actionJoin = new ActionJoinNoWhitespace(); + let start = Position.FromVSCodePosition(vimState.editor.selection.start); + let end = Position.FromVSCodePosition(vimState.editor.selection.end); + + if (start.line === end.line) { + return vimState; + } + + if (start.isAfter(end)) { + [start, end] = [end, start]; + } + + for (let i = start.line; i < end.line; i++) { + vimState = await actionJoin.exec(start, vimState); + } + + return vimState; + } +} + +@RegisterAction +class ActionReplaceCharacter extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['r', '']; + canBeRepeatedWithDot = true; + runsOnceForEachCountPrefix = false; + + public async exec(position: Position, vimState: VimState): Promise { + let timesToRepeat = vimState.recordedState.count || 1; + const toReplace = this.keysPressed[1]; + + /** + * includes , and but not any control keys, + * so we ignore the former two keys and have a special handle for . + */ + + if (['', ''].indexOf(toReplace.toUpperCase()) >= 0) { + return vimState; + } + + if (position.character + timesToRepeat > position.getLineEnd().character) { + return vimState; + } + + let endPos = new Position(position.line, position.character + timesToRepeat); + + // Return if tried to repeat longer than linelength + if (endPos.character > TextEditor.getLineAt(endPos).text.length) { + return vimState; + } + + // If last char (not EOL char), add 1 so that replace selection is complete + if (endPos.character > TextEditor.getLineAt(endPos).text.length) { + endPos = new Position(endPos.line, endPos.character + 1); + } + + if (toReplace === '') { + vimState.recordedState.transformations.push({ + type: 'deleteRange', + range: new Range(position, endPos), + }); + vimState.recordedState.transformations.push({ + type: 'tab', + cursorIndex: this.multicursorIndex, + diff: new PositionDiff(0, -1), + }); + } else { + vimState.recordedState.transformations.push({ + type: 'replaceText', + text: toReplace.repeat(timesToRepeat), + start: position, + end: endPos, + diff: new PositionDiff(0, timesToRepeat - 1), + }); + } + return vimState; + } + + public async execCount(position: Position, vimState: VimState): Promise { + return super.execCount(position, vimState); + } +} + +@RegisterAction +class ActionReplaceCharacterVisual extends BaseCommand { + modes = [ModeName.Visual, ModeName.VisualLine]; + keys = ['r', '']; + runsOnceForEveryCursor() { + return false; + } + canBeRepeatedWithDot = true; + + public async exec(position: Position, vimState: VimState): Promise { + const toInsert = this.keysPressed[1]; + + let visualSelectionOffset = 1; + let start = vimState.cursorStartPosition; + let end = vimState.cursorPosition; + + // If selection is reversed, reorganize it so that the text replace logic always works + if (end.isBeforeOrEqual(start)) { + [start, end] = [end, start]; + } + + // Limit to not replace EOL + const textLength = TextEditor.getLineAt(end).text.length; + if (textLength <= 0) { + visualSelectionOffset = 0; + } + end = new Position(end.line, Math.min(end.character, textLength > 0 ? textLength - 1 : 0)); + + // Iterate over every line in the current selection + for (var lineNum = start.line; lineNum <= end.line; lineNum++) { + // Get line of text + const lineText = TextEditor.getLineAt(new Position(lineNum, 0)).text; + + if (start.line === end.line) { + // This is a visual section all on one line, only replace the part within the selection + vimState.recordedState.transformations.push({ + type: 'replaceText', + text: Array(end.character - start.character + 2).join(toInsert), + start: start, + end: new Position(end.line, end.character + 1), + manuallySetCursorPositions: true, + }); + } else if (lineNum === start.line) { + // This is the first line of the selection so only replace after the cursor + vimState.recordedState.transformations.push({ + type: 'replaceText', + text: Array(lineText.length - start.character + 1).join(toInsert), + start: start, + end: new Position(start.line, lineText.length), + manuallySetCursorPositions: true, + }); + } else if (lineNum === end.line) { + // This is the last line of the selection so only replace before the cursor + vimState.recordedState.transformations.push({ + type: 'replaceText', + text: Array(end.character + 1 + visualSelectionOffset).join(toInsert), + start: new Position(end.line, 0), + end: new Position(end.line, end.character + visualSelectionOffset), + manuallySetCursorPositions: true, + }); + } else { + // Replace the entire line length since it is in the middle of the selection + vimState.recordedState.transformations.push({ + type: 'replaceText', + text: Array(lineText.length + 1).join(toInsert), + start: new Position(lineNum, 0), + end: new Position(lineNum, lineText.length), + manuallySetCursorPositions: true, + }); + } + } + + vimState.cursorPosition = start; + vimState.cursorStartPosition = start; + vimState.currentMode = ModeName.Normal; + return vimState; + } +} + +@RegisterAction +class ActionReplaceCharacterVisualBlock extends BaseCommand { + modes = [ModeName.VisualBlock]; + keys = ['r', '']; + runsOnceForEveryCursor() { + return false; + } + canBeRepeatedWithDot = true; + + public async exec(position: Position, vimState: VimState): Promise { + const toInsert = this.keysPressed[1]; + for (const { start, end } of Position.IterateLine(vimState)) { + if (end.isBeforeOrEqual(start)) { + continue; + } + + vimState.recordedState.transformations.push({ + type: 'replaceText', + text: Array(end.character - start.character + 1).join(toInsert), + start: start, + end: end, + manuallySetCursorPositions: true, + }); + } + + const topLeft = VisualBlockMode.getTopLeftPosition( + vimState.cursorPosition, + vimState.cursorStartPosition + ); + vimState.allCursors = [new Range(topLeft, topLeft)]; + vimState.currentMode = ModeName.Normal; + + return vimState; + } +} + +@RegisterAction +class ActionXVisualBlock extends BaseCommand { + modes = [ModeName.VisualBlock]; + keys = ['x']; + canBeRepeatedWithDot = true; + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + for (const { start, end } of Position.IterateLine(vimState)) { + vimState.recordedState.transformations.push({ + type: 'deleteRange', + range: new Range(start, end), + manuallySetCursorPositions: true, + }); + } + + const topLeft = VisualBlockMode.getTopLeftPosition( + vimState.cursorPosition, + vimState.cursorStartPosition + ); + + vimState.allCursors = [new Range(topLeft, topLeft)]; + vimState.currentMode = ModeName.Normal; + + return vimState; + } +} + +@RegisterAction +class ActionDVisualBlock extends ActionXVisualBlock { + modes = [ModeName.VisualBlock]; + keys = ['d']; + canBeRepeatedWithDot = true; + runsOnceForEveryCursor() { + return false; + } +} + +@RegisterAction +class ActionShiftDVisualBlock extends BaseCommand { + modes = [ModeName.VisualBlock]; + keys = ['D']; + canBeRepeatedWithDot = true; + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + for (const { start } of Position.IterateLine(vimState)) { + vimState.recordedState.transformations.push({ + type: 'deleteRange', + range: new Range(start, start.getLineEnd()), + manuallySetCursorPositions: true, + }); + } + + const topLeft = VisualBlockMode.getTopLeftPosition( + vimState.cursorPosition, + vimState.cursorStartPosition + ); + + vimState.allCursors = [new Range(topLeft, topLeft)]; + vimState.currentMode = ModeName.Normal; + + return vimState; + } +} + +@RegisterAction +class ActionGoToInsertVisualBlockMode extends BaseCommand { + modes = [ModeName.VisualBlock]; + keys = ['I']; + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + vimState.currentMode = ModeName.Insert; + vimState.isMultiCursor = true; + vimState.isFakeMultiCursor = true; + + for (const { line, start } of Position.IterateLine(vimState)) { + if (line === '' && start.character !== 0) { + continue; + } + vimState.allCursors.push(new Range(start, start)); + } + vimState.allCursors = vimState.allCursors.slice(1); + return vimState; + } +} + +@RegisterAction +class ActionChangeInVisualBlockMode extends BaseCommand { + modes = [ModeName.VisualBlock]; + keys = [['c'], ['s']]; + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + for (const { start, end } of Position.IterateLine(vimState)) { + vimState.recordedState.transformations.push({ + type: 'deleteRange', + range: new Range(start, end), + manuallySetCursorPositions: true, + }); + } + + vimState.currentMode = ModeName.Insert; + vimState.isMultiCursor = true; + vimState.isFakeMultiCursor = true; + + for (const { start } of Position.IterateLine(vimState)) { + vimState.allCursors.push(new Range(start, start)); + } + vimState.allCursors = vimState.allCursors.slice(1); + + return vimState; + } +} + +@RegisterAction +class ActionChangeToEOLInVisualBlockMode extends BaseCommand { + modes = [ModeName.VisualBlock]; + keys = [['C'], ['S']]; + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + for (const { start } of Position.IterateLine(vimState)) { + vimState.recordedState.transformations.push({ + type: 'deleteRange', + range: new Range(start, start.getLineEnd()), + collapseRange: true, + }); + } + + vimState.currentMode = ModeName.Insert; + vimState.isMultiCursor = true; + vimState.isFakeMultiCursor = true; + + for (const { end } of Position.IterateLine(vimState)) { + vimState.allCursors.push(new Range(end, end)); + } + vimState.allCursors = vimState.allCursors.slice(1); + + return vimState; + } +} + +@RegisterAction +class ActionGoToInsertVisualBlockModeAppend extends BaseCommand { + modes = [ModeName.VisualBlock]; + keys = ['A']; + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + vimState.currentMode = ModeName.Insert; + vimState.isMultiCursor = true; + vimState.isFakeMultiCursor = true; + + for (const { line, end } of Position.IterateLine(vimState)) { + if (line.trim() === '') { + vimState.recordedState.transformations.push({ + type: 'replaceText', + text: TextEditor.setIndentationLevel(line, end.character), + start: new Position(end.line, 0), + end: new Position(end.line, end.character), + position: new Position(end.line, 0), + }); + } + vimState.allCursors.push(new Range(end, end)); + } + vimState.allCursors = vimState.allCursors.slice(1); + return vimState; + } +} + +@RegisterAction +class ActionDeleteLineVisualMode extends BaseCommand { + modes = [ModeName.Visual, ModeName.VisualLine]; + keys = ['X']; + + public async exec(position: Position, vimState: VimState): Promise { + if (vimState.currentMode === ModeName.Visual) { + return await new operator.DeleteOperator(this.multicursorIndex).run( + vimState, + vimState.cursorStartPosition.getLineBegin(), + vimState.cursorPosition.getLineEnd() + ); + } else { + return await new operator.DeleteOperator(this.multicursorIndex).run( + vimState, + position.getLineBegin(), + position.getLineEnd() + ); + } + } +} + +@RegisterAction +class ActionChangeLineVisualMode extends BaseCommand { + modes = [ModeName.Visual]; + keys = ['C']; + + public async exec(position: Position, vimState: VimState): Promise { + return await new operator.DeleteOperator(this.multicursorIndex).run( + vimState, + vimState.cursorStartPosition.getLineBegin(), + vimState.cursorPosition.getLineEndIncludingEOL() + ); + } +} + +@RegisterAction +class ActionRemoveLineVisualMode extends BaseCommand { + modes = [ModeName.Visual]; + keys = ['R']; + + public async exec(position: Position, vimState: VimState): Promise { + return await new operator.DeleteOperator(this.multicursorIndex).run( + vimState, + vimState.cursorStartPosition.getLineBegin(), + vimState.cursorPosition.getLineEndIncludingEOL() + ); + } +} + +@RegisterAction +class ActionChangeChar extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['s']; + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + const state = await new operator.ChangeOperator().run(vimState, position, position); + + state.currentMode = ModeName.Insert; + + return state; + } + + // Don't clash with surround mode! + public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { + return super.doesActionApply(vimState, keysPressed) && !vimState.recordedState.operator; + } + + public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { + return super.doesActionApply(vimState, keysPressed) && !vimState.recordedState.operator; + } +} + +@RegisterAction +class ToggleCaseAndMoveForward extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['~']; + canBeRepeatedWithDot = true; + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + await new operator.ToggleCaseOperator().run( + vimState, + vimState.cursorPosition, + vimState.cursorPosition + ); + + vimState.cursorPosition = vimState.cursorPosition.getRight(); + return vimState; + } +} + +abstract class IncrementDecrementNumberAction extends BaseCommand { + modes = [ModeName.Normal]; + canBeRepeatedWithDot = true; + offset: number; + + public async exec(position: Position, vimState: VimState): Promise { + const text = TextEditor.getLineAt(position).text; + + // Start looking to the right for the next word to increment, unless we're + // already on a word to increment, in which case start at the beginning of + // that word. + const whereToStart = text[position.character].match(/\s/) + ? position + : position.getWordLeft(true); + + for (let { start, end, word } of Position.IterateWords(whereToStart)) { + // '-' doesn't count as a word, but is important to include in parsing + // the number + if (text[start.character - 1] === '-') { + start = start.getLeft(); + word = text[start.character] + word; + } + // Strict number parsing so "1a" doesn't silently get converted to "1" + do { + const num = NumericString.parse(word); + if ( + num !== null && + position.character < start.character + num.prefix.length + num.value.toString().length + ) { + vimState.cursorPosition = await this.replaceNum( + num, + this.offset * (vimState.recordedState.count || 1), + start, + end + ); + vimState.cursorPosition = vimState.cursorPosition.getLeftByCount(num.suffix.length); + return vimState; + } else if (num !== null) { + word = word.slice(num.prefix.length + num.value.toString().length); + start = new Position( + start.line, + start.character + num.prefix.length + num.value.toString().length + ); + } else { + break; + } + } while (true); + } + // No usable numbers, return the original position + return vimState; + } + + public async replaceNum( + start: NumericString, + offset: number, + startPos: Position, + endPos: Position + ): Promise { + const oldWidth = start.toString().length; + start.value += offset; + const newNum = start.toString(); + + const range = new vscode.Range(startPos, endPos.getRight()); + + if (oldWidth === newNum.length) { + await TextEditor.replace(range, newNum); + } else { + // Can't use replace, since new number is a different width than old + await TextEditor.delete(range); + await TextEditor.insertAt(newNum, startPos); + // Adjust end position according to difference in width of number-string + endPos = new Position(endPos.line, endPos.character + (newNum.length - oldWidth)); + } + + return endPos; + } +} + +@RegisterAction +class IncrementNumberAction extends IncrementDecrementNumberAction { + keys = ['']; + offset = +1; +} + +@RegisterAction +class DecrementNumberAction extends IncrementDecrementNumberAction { + keys = ['']; + offset = -1; +} + +@RegisterAction +class ActionTriggerHover extends BaseCommand { + modes = [ModeName.Normal]; + keys = ['g', 'h']; + runsOnceForEveryCursor() { + return false; + } + + public async exec(position: Position, vimState: VimState): Promise { + await vscode.commands.executeCommand('editor.action.showHover'); + + return vimState; + } +} + +/** + * Multi-Cursor Command Overrides + * + * We currently have to override the vscode key commands that get us into multi-cursor mode. + * + * Normally, we'd just listen for another cursor to be added in order to go into multi-cursor + * mode rather than rewriting each keybinding one-by-one. We can't currently do that because + * Visual Block Mode also creates additional cursors, but will get confused if you're in + * multi-cursor mode. + */ + +@RegisterAction +class ActionOverrideCmdD extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual]; + keys = [[''], ['g', 'b']]; + runsOnceForEveryCursor() { + return false; + } + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + await vscode.commands.executeCommand('editor.action.addSelectionToNextFindMatch'); + vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); + + // If this is the first cursor, select 1 character less + // so that only the word is selected, no extra character + vimState.allCursors = vimState.allCursors.map(x => x.withNewStop(x.stop.getLeft())); + + vimState.currentMode = ModeName.Visual; + + return vimState; + } +} + +@RegisterAction +class ActionOverrideCmdDInsert extends BaseCommand { + modes = [ModeName.Insert]; + keys = ['']; + runsOnceForEveryCursor() { + return false; + } + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + // Since editor.action.addSelectionToNextFindMatch uses the selection to + // determine where to add a word, we need to do a hack and manually set the + // selections to the word boundaries before we make the api call. + vscode.window.activeTextEditor!.selections = vscode.window + .activeTextEditor!.selections.map((x, idx) => { + const curPos = Position.FromVSCodePosition(x.active); + if (idx === 0) { + return new vscode.Selection( + curPos.getWordLeft(false), + curPos + .getLeft() + .getCurrentWordEnd(true) + .getRight() + ); + } else { + // Since we're adding the selections ourselves, we need to make sure + // that our selection is actually over what our original word is + const matchWordPos = Position.FromVSCodePosition( + vscode.window.activeTextEditor!.selections[0].active + ); + const matchWordLength = + matchWordPos + .getLeft() + .getCurrentWordEnd(true) + .getRight().character - matchWordPos.getWordLeft(false).character; + const wordBegin = curPos.getLeftByCount(matchWordLength); + return new vscode.Selection(wordBegin, curPos); + } + }); + await vscode.commands.executeCommand('editor.action.addSelectionToNextFindMatch'); + vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); + return vimState; + } +} + +@RegisterAction +class ActionOverrideCmdAltDown extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual]; + keys = [ + [''], // OSX + [''], // Windows + ]; + runsOnceForEveryCursor() { + return false; + } + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + await vscode.commands.executeCommand('editor.action.insertCursorBelow'); + vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); + + return vimState; + } +} + +@RegisterAction +class ActionOverrideCmdAltUp extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual]; + keys = [ + [''], // OSX + [''], // Windows + ]; + runsOnceForEveryCursor() { + return false; + } + runsOnceForEachCountPrefix = true; + + public async exec(position: Position, vimState: VimState): Promise { + await vscode.commands.executeCommand('editor.action.insertCursorAbove'); + vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); + + return vimState; + } +} diff --git a/src/actions/motion.ts b/src/actions/motion.ts index d7ef40cf476..36a6be3a025 100644 --- a/src/actions/motion.ts +++ b/src/actions/motion.ts @@ -1,1844 +1,1835 @@ -import * as vscode from 'vscode'; -import { ModeName } from './../mode/mode'; -import { Position, PositionDiff } from './../common/motion/position'; -import { Configuration } from './../configuration/configuration'; -import { TextEditor, CursorMovePosition, CursorMoveByUnit } from './../textEditor'; -import { VimState } from './../mode/modeHandler'; -import { RegisterMode } from './../register/register'; -import { PairMatcher } from './../common/matching/matcher'; -import { ReplaceState } from './../state/replaceState'; -import { QuoteMatcher } from './../common/matching/quoteMatcher'; -import { TagMatcher } from './../common/matching/tagMatcher'; -import { RegisterAction } from './base'; -import { ChangeOperator } from './operator'; -import { BaseAction } from './base'; - -export function isIMovement(o: IMovement | Position): o is IMovement { - return (o as IMovement).start !== undefined && (o as IMovement).stop !== undefined; -} - -/** - * The result of a (more sophisticated) Movement. - */ -export interface IMovement { - start: Position; - stop: Position; - - /** - * Whether this motion succeeded. Some commands, like fx when 'x' can't be found, - * will not move the cursor. Furthermore, dfx won't delete *anything*, even though - * deleting to the current character would generally delete 1 character. - */ - failed?: boolean; - - diff?: PositionDiff; - - // It /so/ annoys me that I have to put this here. - registerMode?: RegisterMode; -} - -/** - * A movement is something like 'h', 'k', 'w', 'b', 'gg', etc. - */ -export abstract class BaseMovement extends BaseAction { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - - isMotion = true; - - canBePrefixedWithCount = false; - - /** - * Whether we should change lastRepeatableMovement in VimState. - */ - public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { - return false; - } - - /** - * Whether we should change desiredColumn in VimState. - */ - public doesntChangeDesiredColumn = false; - - /** - * This is for commands like $ which force the desired column to be at - * the end of even the longest line. - */ - public setsDesiredColumnToEOL = false; - - /** - * Run the movement a single time. - * - * Generally returns a new Position. If necessary, it can return an IMovement instead. - * Note: If returning an IMovement, make sure that repeated actions on a - * visual selection work. For example, V}} - */ - public async execAction(position: Position, vimState: VimState): Promise { - throw new Error('Not implemented!'); - } - - /** - * Run the movement in an operator context a single time. - * - * Some movements operate over different ranges when used for operators. - */ - public async execActionForOperator( - position: Position, - vimState: VimState - ): Promise { - return await this.execAction(position, vimState); - } - - /** - * Run a movement count times. - * - * count: the number prefix the user entered, or 0 if they didn't enter one. - */ - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - let recordedState = vimState.recordedState; - let result: Position | IMovement = new Position(0, 0); // bogus init to satisfy typechecker - - if (count < 1) { - count = 1; - } else if (count > 99999) { - count = 99999; - } - - for (let i = 0; i < count; i++) { - const firstIteration = i === 0; - const lastIteration = i === count - 1; - const temporaryResult = - recordedState.operator && lastIteration - ? await this.execActionForOperator(position, vimState) - : await this.execAction(position, vimState); - - if (temporaryResult instanceof Position) { - result = temporaryResult; - position = temporaryResult; - } else if (isIMovement(temporaryResult)) { - if (result instanceof Position) { - result = { - start: new Position(0, 0), - stop: new Position(0, 0), - failed: false, - }; - } - - result.failed = result.failed || temporaryResult.failed; - - if (firstIteration) { - (result as IMovement).start = temporaryResult.start; - } - - if (lastIteration) { - (result as IMovement).stop = temporaryResult.stop; - } else { - position = temporaryResult.stop.getRightThroughLineBreaks(); - } - - result.registerMode = temporaryResult.registerMode; - } - } - - return result; - } -} - -abstract class MoveByScreenLine extends BaseMovement { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - movementType: CursorMovePosition; - by: CursorMoveByUnit; - value: number = 1; - - public async execAction(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand('cursorMove', { - to: this.movementType, - select: vimState.currentMode !== ModeName.Normal, - by: this.by, - value: this.value, - }); - - if (vimState.currentMode === ModeName.Normal) { - return Position.FromVSCodePosition(vimState.editor.selection.active); - } else { - /** - * cursorMove command is handling the selection for us. - * So we are not following our design principal (do no real movement inside an action) here. - */ - let start = Position.FromVSCodePosition(vimState.editor.selection.start); - let stop = Position.FromVSCodePosition(vimState.editor.selection.end); - let curPos = Position.FromVSCodePosition(vimState.editor.selection.active); - - // We want to swap the cursor start stop positions based on which direction we are moving, up or down - if (start.isEqual(curPos)) { - position = start; - [start, stop] = [stop, start]; - start = start.getLeft(); - } - - return { start, stop }; - } - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand('cursorMove', { - to: this.movementType, - select: true, - by: this.by, - value: this.value, - }); - - return { - start: Position.FromVSCodePosition(vimState.editor.selection.start), - stop: Position.FromVSCodePosition(vimState.editor.selection.end), - }; - } -} - -abstract class MoveByScreenLineMaintainDesiredColumn extends MoveByScreenLine { - doesntChangeDesiredColumn = true; - public async execAction(position: Position, vimState: VimState): Promise { - let prevDesiredColumn = vimState.desiredColumn; - let prevLine = vimState.editor.selection.active.line; - - await vscode.commands.executeCommand('cursorMove', { - to: this.movementType, - select: vimState.currentMode !== ModeName.Normal, - by: this.by, - value: this.value, - }); - - if (vimState.currentMode === ModeName.Normal) { - let returnedPos = Position.FromVSCodePosition(vimState.editor.selection.active); - if (prevLine !== returnedPos.line) { - returnedPos = returnedPos.setLocation(returnedPos.line, prevDesiredColumn); - } - return returnedPos; - } else { - /** - * cursorMove command is handling the selection for us. - * So we are not following our design principal (do no real movement inside an action) here. - */ - let start = Position.FromVSCodePosition(vimState.editor.selection.start); - let stop = Position.FromVSCodePosition(vimState.editor.selection.end); - let curPos = Position.FromVSCodePosition(vimState.editor.selection.active); - - // We want to swap the cursor start stop positions based on which direction we are moving, up or down - if (start.isEqual(curPos)) { - position = start; - [start, stop] = [stop, start]; - start = start.getLeft(); - } - - return { start, stop }; - } - } -} - -class MoveDownByScreenLineMaintainDesiredColumn extends MoveByScreenLineMaintainDesiredColumn { - movementType: CursorMovePosition = 'down'; - by: CursorMoveByUnit = 'wrappedLine'; - value = 1; -} - -class MoveDownFoldFix extends MoveByScreenLineMaintainDesiredColumn { - movementType: CursorMovePosition = 'down'; - by: CursorMoveByUnit = 'line'; - value = 1; - - public async execAction(position: Position, vimState: VimState): Promise { - if (position.line === TextEditor.getLineCount() - 1) { - return position; - } - let t: Position; - let count = 0; - const prevDesiredColumn = vimState.desiredColumn; - do { - t = await new MoveDownByScreenLine().execAction(position, vimState); - count += 1; - } while (t.line === position.line); - if (t.line > position.line + 1) { - return t; - } - while (count > 0) { - t = await new MoveUpByScreenLine().execAction(position, vimState); - count--; - } - vimState.desiredColumn = prevDesiredColumn; - return await position.getDown(vimState.desiredColumn); - } -} - -@RegisterAction -class MoveDown extends BaseMovement { - keys = ['j']; - doesntChangeDesiredColumn = true; - - public async execAction(position: Position, vimState: VimState): Promise { - if (Configuration.foldfix && vimState.currentMode !== ModeName.VisualBlock) { - return new MoveDownFoldFix().execAction(position, vimState); - } - return position.getDown(vimState.desiredColumn); - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - vimState.currentRegisterMode = RegisterMode.LineWise; - return position.getDown(position.getLineEnd().character); - } -} - -@RegisterAction -class MoveDownArrow extends MoveDown { - keys = ['']; -} - -class MoveUpByScreenLineMaintainDesiredColumn extends MoveByScreenLineMaintainDesiredColumn { - movementType: CursorMovePosition = 'up'; - by: CursorMoveByUnit = 'wrappedLine'; - value = 1; -} - -@RegisterAction -class MoveUp extends BaseMovement { - keys = ['k']; - doesntChangeDesiredColumn = true; - - public async execAction(position: Position, vimState: VimState): Promise { - if (Configuration.foldfix && vimState.currentMode !== ModeName.VisualBlock) { - return new MoveUpFoldFix().execAction(position, vimState); - } - return position.getUp(vimState.desiredColumn); - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - vimState.currentRegisterMode = RegisterMode.LineWise; - return position.getUp(position.getLineEnd().character); - } -} - -@RegisterAction -class MoveUpFoldFix extends MoveByScreenLineMaintainDesiredColumn { - movementType: CursorMovePosition = 'up'; - by: CursorMoveByUnit = 'line'; - value = 1; - - public async execAction(position: Position, vimState: VimState): Promise { - if (position.line === 0) { - return position; - } - let t: Position; - const prevDesiredColumn = vimState.desiredColumn; - let count = 0; - - do { - t = await new MoveUpByScreenLineMaintainDesiredColumn().execAction( - position, - vimState - ); - count += 1; - } while (t.line === position.line); - vimState.desiredColumn = prevDesiredColumn; - if (t.line < position.line - 1) { - return t; - } - while (count > 0) { - t = await new MoveDownByScreenLine().execAction(position, vimState); - count--; - } - vimState.desiredColumn = prevDesiredColumn; - return await position.getUp(vimState.desiredColumn); - } -} - -@RegisterAction -class MoveUpArrow extends MoveUp { - keys = ['']; -} - -@RegisterAction -class ArrowsInReplaceMode extends BaseMovement { - modes = [ModeName.Replace]; - keys = [[''], [''], [''], ['']]; - - public async execAction(position: Position, vimState: VimState): Promise { - let newPosition: Position = position; - - switch (this.keysPressed[0]) { - case '': - newPosition = await new MoveUpArrow().execAction(position, vimState); - break; - case '': - newPosition = await new MoveDownArrow().execAction(position, vimState); - break; - case '': - newPosition = await new MoveLeftArrow().execAction(position, vimState); - break; - case '': - newPosition = await new MoveRightArrow().execAction(position, vimState); - break; - default: - break; - } - vimState.replaceState = new ReplaceState(newPosition); - return newPosition; - } -} - -@RegisterAction -class UpArrowInReplaceMode extends ArrowsInReplaceMode { - keys = [['']]; -} - -@RegisterAction -class DownArrowInReplaceMode extends ArrowsInReplaceMode { - keys = [['']]; -} - -@RegisterAction -class LeftArrowInReplaceMode extends ArrowsInReplaceMode { - keys = [['']]; -} - -@RegisterAction -class RightArrowInReplaceMode extends ArrowsInReplaceMode { - keys = [['']]; -} - -@RegisterAction -class CommandNextSearchMatch extends BaseMovement { - keys = ['n']; - - public async execAction(position: Position, vimState: VimState): Promise { - const searchState = vimState.globalState.searchState; - - if (!searchState || searchState.searchString === '') { - return position; - } - // Turn one of the highlighting flags back on (turned off with :nohl) - vimState.globalState.hl = true; - - if (vimState.cursorPosition.getRight().isEqual(vimState.cursorPosition.getLineEnd())) { - return searchState.getNextSearchMatchPosition(vimState.cursorPosition.getRight()).pos; - } - - // Turn one of the highlighting flags back on (turned off with :nohl) - - return searchState.getNextSearchMatchPosition(vimState.cursorPosition).pos; - } -} - -@RegisterAction -class CommandPreviousSearchMatch extends BaseMovement { - keys = ['N']; - - public async execAction(position: Position, vimState: VimState): Promise { - const searchState = vimState.globalState.searchState; - - if (!searchState || searchState.searchString === '') { - return position; - } - - // Turn one of the highlighting flags back on (turned off with :nohl) - vimState.globalState.hl = true; - - return searchState.getNextSearchMatchPosition(vimState.cursorPosition, -1).pos; - } -} - -@RegisterAction -export class MarkMovementBOL extends BaseMovement { - keys = ["'", '']; - - public async execAction(position: Position, vimState: VimState): Promise { - const markName = this.keysPressed[1]; - const mark = vimState.historyTracker.getMark(markName); - - vimState.currentRegisterMode = RegisterMode.LineWise; - - return mark.position.getFirstLineNonBlankChar(); - } -} - -@RegisterAction -export class MarkMovement extends BaseMovement { - keys = ['`', '']; - - public async execAction(position: Position, vimState: VimState): Promise { - const markName = this.keysPressed[1]; - const mark = vimState.historyTracker.getMark(markName); - - return mark.position; - } -} - -@RegisterAction -export class MoveLeft extends BaseMovement { - keys = ['h']; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getLeft(); - } -} - -@RegisterAction -class MoveLeftArrow extends MoveLeft { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ['']; -} - -@RegisterAction -class BackSpaceInNormalMode extends BaseMovement { - modes = [ModeName.Normal]; - keys = ['']; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getLeftThroughLineBreaks(); - } -} - -@RegisterAction -class MoveRight extends BaseMovement { - keys = ['l']; - - public async execAction(position: Position, vimState: VimState): Promise { - return new Position(position.line, position.character + 1); - } -} - -@RegisterAction -class MoveRightArrow extends MoveRight { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ['']; -} - -@RegisterAction -class MoveRightWithSpace extends BaseMovement { - keys = [' ']; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getRightThroughLineBreaks(); - } -} - -@RegisterAction -class MoveDownNonBlank extends BaseMovement { - keys = ['+']; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - return position.getDownByCount(Math.max(count, 1)).getFirstLineNonBlankChar(); - } -} - -@RegisterAction -class MoveUpNonBlank extends BaseMovement { - keys = ['-']; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - return position.getUpByCount(Math.max(count, 1)).getFirstLineNonBlankChar(); - } -} - -@RegisterAction -class MoveDownUnderscore extends BaseMovement { - keys = ['_']; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - return position.getDownByCount(Math.max(count - 1, 0)).getFirstLineNonBlankChar(); - } -} - -@RegisterAction -class MoveToColumn extends BaseMovement { - keys = ['|']; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - return new Position(position.line, Math.max(0, count - 1)); - } -} - -@RegisterAction -class MoveFindForward extends BaseMovement { - keys = ['f', '']; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - count = count || 1; - const toFind = this.keysPressed[1]; - let result = position.findForwards(toFind, count); - - if (!result) { - return { start: position, stop: position, failed: true }; - } - - if (vimState.recordedState.operator) { - result = result.getRight(); - } - - return result; - } - - public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { - return !vimState.recordedState.operator || !(isIMovement(result) && result.failed); - } -} - -@RegisterAction -class MoveFindBackward extends BaseMovement { - keys = ['F', '']; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - count = count || 1; - const toFind = this.keysPressed[1]; - let result = position.findBackwards(toFind, count); - - if (!result) { - return { start: position, stop: position, failed: true }; - } - - return result; - } - - public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { - return !vimState.recordedState.operator || !(isIMovement(result) && result.failed); - } -} - -@RegisterAction -class MoveTilForward extends BaseMovement { - keys = ['t', '']; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - count = count || 1; - const toFind = this.keysPressed[1]; - let result = position.tilForwards(toFind, count); - - if (!result) { - return { start: position, stop: position, failed: true }; - } - - if (vimState.recordedState.operator) { - result = result.getRight(); - } - - return result; - } - - public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { - return !vimState.recordedState.operator || !(isIMovement(result) && result.failed); - } -} - -@RegisterAction -class MoveTilBackward extends BaseMovement { - keys = ['T', '']; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - count = count || 1; - const toFind = this.keysPressed[1]; - let result = position.tilBackwards(toFind, count); - - if (!result) { - return { start: position, stop: position, failed: true }; - } - - return result; - } - - public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { - return !vimState.recordedState.operator || !(isIMovement(result) && result.failed); - } -} - -@RegisterAction -class MoveRepeat extends BaseMovement { - keys = [';']; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - const movement = VimState.lastRepeatableMovement; - if (movement) { - const result = await movement.execActionWithCount(position, vimState, count); - /** - * For t and T commands vim executes ; as 2; - * This way the cursor will get to the next instance of - */ - if (result instanceof Position && position.isEqual(result) && count <= 1) { - return await movement.execActionWithCount(position, vimState, 2); - } - return result; - } - return position; - } -} - -@RegisterAction -class MoveRepeatReversed extends BaseMovement { - keys = [',']; - static reverseMotionMapping: Map BaseMovement> = new Map([ - [MoveFindForward, () => new MoveFindBackward()], - [MoveFindBackward, () => new MoveFindForward()], - [MoveTilForward, () => new MoveTilBackward()], - [MoveTilBackward, () => new MoveTilForward()], - ]); - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - const movement = VimState.lastRepeatableMovement; - if (movement) { - const reverse = MoveRepeatReversed.reverseMotionMapping.get(movement.constructor)!(); - reverse.keysPressed = [(reverse.keys as string[])[0], movement.keysPressed[1]]; - - let result = await reverse.execActionWithCount(position, vimState, count); - // For t and T commands vim executes ; as 2; - if (result instanceof Position && position.isEqual(result) && count <= 1) { - result = await reverse.execActionWithCount(position, vimState, 2); - } - return result; - } - return position; - } -} - -@RegisterAction -class MoveLineEnd extends BaseMovement { - keys = [['$'], [''], ['']]; - setsDesiredColumnToEOL = true; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - return position.getDownByCount(Math.max(count - 1, 0)).getLineEnd(); - } -} - -@RegisterAction -class MoveLineBegin extends BaseMovement { - keys = [['0'], [''], ['']]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getLineBegin(); - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.doesActionApply(vimState, keysPressed) && vimState.recordedState.count === 0; - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.couldActionApply(vimState, keysPressed) && vimState.recordedState.count === 0; - } -} - -@RegisterAction -class MoveScreenLineBegin extends MoveByScreenLine { - keys = ['g', '0']; - movementType: CursorMovePosition = 'wrappedLineStart'; -} - -@RegisterAction -class MoveScreenNonBlank extends MoveByScreenLine { - keys = ['g', '^']; - movementType: CursorMovePosition = 'wrappedLineFirstNonWhitespaceCharacter'; -} - -@RegisterAction -class MoveScreenLineEnd extends MoveByScreenLine { - keys = ['g', '$']; - movementType: CursorMovePosition = 'wrappedLineEnd'; -} - -@RegisterAction -class MoveScreenLineEndNonBlank extends MoveByScreenLine { - keys = ['g', '_']; - movementType: CursorMovePosition = 'wrappedLineLastNonWhitespaceCharacter'; - canBePrefixedWithCount = true; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - count = count || 1; - const pos = await this.execAction(position, vimState); - const newPos: Position | IMovement = pos as Position; - - // If in visual, return a selection - if (pos instanceof Position) { - return pos.getDownByCount(count - 1); - } else if (isIMovement(pos)) { - return { start: pos.start, stop: pos.stop.getDownByCount(count - 1).getLeft() }; - } - - return newPos.getDownByCount(count - 1); - } -} - -@RegisterAction -class MoveScreenLineCenter extends MoveByScreenLine { - keys = ['g', 'm']; - movementType: CursorMovePosition = 'wrappedLineColumnCenter'; -} - -@RegisterAction -export class MoveUpByScreenLine extends MoveByScreenLine { - modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual]; - keys = [['g', 'k'], ['g', '']]; - movementType: CursorMovePosition = 'up'; - by: CursorMoveByUnit = 'wrappedLine'; - value = 1; -} - -@RegisterAction -class MoveDownByScreenLine extends MoveByScreenLine { - modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual]; - keys = [['g', 'j'], ['g', '']]; - movementType: CursorMovePosition = 'down'; - by: CursorMoveByUnit = 'wrappedLine'; - value = 1; -} - -// Because we can't support moving by screen line when in visualLine mode, -// we change to moving by regular line in visualLine mode. We can't move by -// screen line is that our ranges only support a start and stop attribute, -// and moving by screen line just snaps us back to the original position. -// Check PR #1600 for discussion. -@RegisterAction -class MoveUpByScreenLineVisualLine extends MoveByScreenLine { - modes = [ModeName.VisualLine]; - keys = [['g', 'k'], ['g', '']]; - movementType: CursorMovePosition = 'up'; - by: CursorMoveByUnit = 'line'; - value = 1; -} - -@RegisterAction -class MoveDownByScreenLineVisualLine extends MoveByScreenLine { - modes = [ModeName.VisualLine]; - keys = [['g', 'j'], ['g', '']]; - movementType: CursorMovePosition = 'down'; - by: CursorMoveByUnit = 'line'; - value = 1; -} - -@RegisterAction -class MoveScreenToRight extends MoveByScreenLine { - modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ['z', 'h']; - movementType: CursorMovePosition = 'right'; - by: CursorMoveByUnit = 'character'; - value = 1; -} - -@RegisterAction -class MoveScreenToLeft extends MoveByScreenLine { - modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ['z', 'l']; - movementType: CursorMovePosition = 'left'; - by: CursorMoveByUnit = 'character'; - value = 1; -} - -@RegisterAction -class MoveScreenToRightHalf extends MoveByScreenLine { - modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ['z', 'H']; - movementType: CursorMovePosition = 'right'; - by: CursorMoveByUnit = 'halfLine'; - value = 1; -} - -@RegisterAction -class MoveScreenToLeftHalf extends MoveByScreenLine { - modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ['z', 'L']; - movementType: CursorMovePosition = 'left'; - by: CursorMoveByUnit = 'halfLine'; - value = 1; -} - -@RegisterAction -class MoveToLineFromViewPortTop extends MoveByScreenLine { - keys = ['H']; - movementType: CursorMovePosition = 'viewPortTop'; - by: CursorMoveByUnit = 'line'; - value = 1; - canBePrefixedWithCount = true; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - this.value = count < 1 ? 1 : count; - return await this.execAction(position, vimState); - } -} - -@RegisterAction -class MoveToLineFromViewPortBottom extends MoveByScreenLine { - keys = ['L']; - movementType: CursorMovePosition = 'viewPortBottom'; - by: CursorMoveByUnit = 'line'; - value = 1; - canBePrefixedWithCount = true; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - this.value = count < 1 ? 1 : count; - return await this.execAction(position, vimState); - } -} - -@RegisterAction -class MoveToMiddleLineInViewPort extends MoveByScreenLine { - keys = ['M']; - movementType: CursorMovePosition = 'viewPortCenter'; - by: CursorMoveByUnit = 'line'; -} - -@RegisterAction -class MoveNonBlank extends BaseMovement { - keys = ['^']; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getFirstLineNonBlankChar(); - } -} - -@RegisterAction -class MoveNextLineNonBlank extends BaseMovement { - keys = ['\n']; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - vimState.currentRegisterMode = RegisterMode.LineWise; - - // Count === 0 if just pressing enter in normal mode, need to still go down 1 line - if (count === 0) { - count++; - } - - return position.getDownByCount(count).getFirstLineNonBlankChar(); - } -} - -@RegisterAction -class MoveNonBlankFirst extends BaseMovement { - keys = ['g', 'g']; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - if (count === 0) { - return position.getDocumentBegin().getFirstLineNonBlankChar(); - } - - return new Position(count - 1, 0).getFirstLineNonBlankChar(); - } -} - -@RegisterAction -class MoveNonBlankLast extends BaseMovement { - keys = ['G']; - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - let stop: Position; - - if (count === 0) { - stop = new Position(TextEditor.getLineCount() - 1, 0); - } else { - stop = new Position(Math.min(count, TextEditor.getLineCount()) - 1, 0); - } - - return { - start: vimState.cursorStartPosition, - stop: stop, - registerMode: RegisterMode.LineWise, - }; - } -} - -@RegisterAction -export class MoveWordBegin extends BaseMovement { - keys = ['w']; - - public async execAction( - position: Position, - vimState: VimState, - isLastIteration: boolean = false - ): Promise { - if (isLastIteration && vimState.recordedState.operator instanceof ChangeOperator) { - if (TextEditor.getLineAt(position).text.length < 1) { - return position; - } - - const line = TextEditor.getLineAt(position).text; - const char = line[position.character]; - - /* - From the Vim manual: - - Special case: "cw" and "cW" are treated like "ce" and "cE" if the cursor is - on a non-blank. This is because "cw" is interpreted as change-word, and a - word does not include the following white space. - */ - - if (' \t'.indexOf(char) >= 0) { - return position.getWordRight(); - } else { - return position.getCurrentWordEnd(true).getRight(); - } - } else { - return position.getWordRight(); - } - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - const result = await this.execAction(position, vimState, true); - - /* - From the Vim documentation: - - Another special case: When using the "w" motion in combination with an - operator and the last word moved over is at the end of a line, the end of - that word becomes the end of the operated text, not the first word in the - next line. - */ - - if ( - result.line > position.line + 1 || - (result.line === position.line + 1 && result.isFirstWordOfLine()) - ) { - return position.getLineEnd(); - } - - if (result.isLineEnd()) { - return new Position(result.line, result.character + 1); - } - - return result; - } -} - -@RegisterAction -class MoveFullWordBegin extends BaseMovement { - keys = ['W']; - - public async execAction(position: Position, vimState: VimState): Promise { - if (vimState.recordedState.operator instanceof ChangeOperator) { - // TODO use execForOperator? Or maybe dont? - - // See note for w - return position.getCurrentBigWordEnd().getRight(); - } else { - return position.getBigWordRight(); - } - } -} - -@RegisterAction -class MoveWordEnd extends BaseMovement { - keys = ['e']; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getCurrentWordEnd(); - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - let end = position.getCurrentWordEnd(); - - return new Position(end.line, end.character + 1); - } -} - -@RegisterAction -class MoveFullWordEnd extends BaseMovement { - keys = ['E']; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getCurrentBigWordEnd(); - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - return position.getCurrentBigWordEnd().getRight(); - } -} - -@RegisterAction -class MoveLastWordEnd extends BaseMovement { - keys = ['g', 'e']; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getLastWordEnd(); - } -} - -@RegisterAction -class MoveLastFullWordEnd extends BaseMovement { - keys = ['g', 'E']; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getLastBigWordEnd(); - } -} - -@RegisterAction -class MoveBeginningWord extends BaseMovement { - keys = ['b']; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getWordLeft(); - } -} - -@RegisterAction -class MoveBeginningFullWord extends BaseMovement { - keys = ['B']; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getBigWordLeft(); - } -} - -@RegisterAction -class MovePreviousSentenceBegin extends BaseMovement { - keys = ['(']; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getSentenceBegin({ forward: false }); - } -} - -@RegisterAction -class MoveNextSentenceBegin extends BaseMovement { - keys = [')']; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getSentenceBegin({ forward: true }); - } -} - -@RegisterAction -class MoveParagraphEnd extends BaseMovement { - keys = ['}']; - - public async execAction(position: Position, vimState: VimState): Promise { - const isLineWise = - position.isLineBeginning() && - vimState.currentMode === ModeName.Normal && - vimState.recordedState.operator; - let paragraphEnd = position.getCurrentParagraphEnd(); - vimState.currentRegisterMode = isLineWise - ? RegisterMode.LineWise - : RegisterMode.FigureItOutFromCurrentMode; - return isLineWise ? paragraphEnd.getLeftThroughLineBreaks(true) : paragraphEnd; - } -} - -@RegisterAction -class MoveParagraphBegin extends BaseMovement { - keys = ['{']; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getCurrentParagraphBeginning(); - } -} - -abstract class MoveSectionBoundary extends BaseMovement { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - boundary: string; - forward: boolean; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getSectionBoundary({ - forward: this.forward, - boundary: this.boundary, - }); - } -} - -@RegisterAction -class MoveNextSectionBegin extends MoveSectionBoundary { - keys = [']', ']']; - boundary = '{'; - forward = true; -} - -@RegisterAction -class MoveNextSectionEnd extends MoveSectionBoundary { - keys = [']', '[']; - boundary = '}'; - forward = true; -} - -@RegisterAction -class MovePreviousSectionBegin extends MoveSectionBoundary { - keys = ['[', '[']; - boundary = '{'; - forward = false; -} - -@RegisterAction -class MovePreviousSectionEnd extends MoveSectionBoundary { - keys = ['[', ']']; - boundary = '}'; - forward = false; -} - -@RegisterAction -class MoveToMatchingBracket extends BaseMovement { - keys = ['%']; - - public async execAction(position: Position, vimState: VimState): Promise { - position = position.getLeftIfEOL(); - - const text = TextEditor.getLineAt(position).text; - const charToMatch = text[position.character]; - const toFind = PairMatcher.pairings[charToMatch]; - const failure = { start: position, stop: position, failed: true }; - - if (!toFind || !toFind.matchesWithPercentageMotion) { - // If we're not on a match, go right until we find a - // pairable character or hit the end of line. - - for (let i = position.character; i < text.length; i++) { - if (PairMatcher.pairings[text[i]]) { - // We found an opening char, now move to the matching closing char - const openPosition = new Position(position.line, i); - const result = PairMatcher.nextPairedChar(openPosition, text[i], true); - - if (!result) { - return failure; - } - return result; - } - } - - return failure; - } - - const result = PairMatcher.nextPairedChar(position, charToMatch, true); - if (!result) { - return failure; - } - return result; - } - - public async execActionForOperator( - position: Position, - vimState: VimState - ): Promise { - const result = await this.execAction(position, vimState); - - if (isIMovement(result)) { - if (result.failed) { - return result; - } else { - throw new Error('Did not ever handle this case!'); - } - } - - if (position.compareTo(result) > 0) { - return { - start: result, - stop: position.getRight(), - }; - } else { - return result.getRight(); - } - } - - public async execActionWithCount( - position: Position, - vimState: VimState, - count: number - ): Promise { - // % has a special mode that lets you use it to jump to a percentage of the file - // However, some other bracket motions inherit from this so only do this behavior for % explicitly - if (Object.getPrototypeOf(this) === MoveToMatchingBracket.prototype) { - if (count === 0) { - if (vimState.recordedState.operator) { - return this.execActionForOperator(position, vimState); - } else { - return this.execAction(position, vimState); - } - } - - // Check to make sure this is a valid percentage - if (count < 0 || count > 100) { - return { start: position, stop: position, failed: true }; - } - - const targetLine = Math.round(count * TextEditor.getLineCount() / 100); - return new Position(targetLine - 1, 0).getFirstLineNonBlankChar(); - } else { - return super.execActionWithCount(position, vimState, count); - } - } -} - -export abstract class MoveInsideCharacter extends BaseMovement { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - protected charToMatch: string; - protected includeSurrounding = false; - - public async execAction(position: Position, vimState: VimState): Promise { - const failure = { start: position, stop: position, failed: true }; - const text = TextEditor.getLineAt(position).text; - const closingChar = PairMatcher.pairings[this.charToMatch].match; - const closedMatch = text[position.character] === closingChar; - - // First, search backwards for the opening character of the sequence - let startPos = PairMatcher.nextPairedChar(position, closingChar, closedMatch); - if (startPos === undefined) { - return failure; - } - - let startPlusOne: Position; - - if (startPos.isAfterOrEqual(startPos.getLineEnd().getLeft())) { - startPlusOne = new Position(startPos.line + 1, 0); - } else { - startPlusOne = new Position(startPos.line, startPos.character + 1); - } - - let endPos = PairMatcher.nextPairedChar(startPlusOne, this.charToMatch, false); - if (endPos === undefined) { - return failure; - } - - if (this.includeSurrounding) { - if (vimState.currentMode !== ModeName.Visual) { - endPos = new Position(endPos.line, endPos.character + 1); - } - } else { - startPos = startPlusOne; - if (vimState.currentMode === ModeName.Visual) { - endPos = endPos.getLeftThroughLineBreaks(); - } - } - - // If the closing character is the first on the line, don't swallow it. - if (!this.includeSurrounding) { - if (endPos.getLeft().isInLeadingWhitespace()) { - endPos = endPos.getLineBegin(); - } - } - - if (position.isBefore(startPos)) { - vimState.recordedState.operatorPositionDiff = startPos.subtract(position); - } - - return { - start: startPos, - stop: endPos, - diff: new PositionDiff(0, startPos === position ? 1 : 0), - }; - } - - public async execActionForOperator( - position: Position, - vimState: VimState - ): Promise { - const result = await this.execAction(position, vimState); - if (isIMovement(result)) { - if (result.failed) { - vimState.recordedState.hasRunOperator = false; - vimState.recordedState.actionsRun = []; - } - } - return result; - } -} - -@RegisterAction -class MoveIParentheses extends MoveInsideCharacter { - keys = ['i', '(']; - charToMatch = '('; -} - -@RegisterAction -class MoveIClosingParentheses extends MoveInsideCharacter { - keys = ['i', ')']; - charToMatch = '('; -} - -@RegisterAction -class MoveIClosingParenthesesBlock extends MoveInsideCharacter { - keys = ['i', 'b']; - charToMatch = '('; -} - -@RegisterAction -export class MoveAParentheses extends MoveInsideCharacter { - keys = ['a', '(']; - charToMatch = '('; - includeSurrounding = true; -} - -@RegisterAction -class MoveAClosingParentheses extends MoveInsideCharacter { - keys = ['a', ')']; - charToMatch = '('; - includeSurrounding = true; -} - -@RegisterAction -class MoveAParenthesesBlock extends MoveInsideCharacter { - keys = ['a', 'b']; - charToMatch = '('; - includeSurrounding = true; -} - -@RegisterAction -class MoveICurlyBrace extends MoveInsideCharacter { - keys = ['i', '{']; - charToMatch = '{'; -} - -@RegisterAction -class MoveIClosingCurlyBrace extends MoveInsideCharacter { - keys = ['i', '}']; - charToMatch = '{'; -} - -@RegisterAction -class MoveIClosingCurlyBraceBlock extends MoveInsideCharacter { - keys = ['i', 'B']; - charToMatch = '{'; -} - -@RegisterAction -export class MoveACurlyBrace extends MoveInsideCharacter { - keys = ['a', '{']; - charToMatch = '{'; - includeSurrounding = true; -} - -@RegisterAction -export class MoveAClosingCurlyBrace extends MoveInsideCharacter { - keys = ['a', '}']; - charToMatch = '{'; - includeSurrounding = true; -} - -@RegisterAction -class MoveAClosingCurlyBraceBlock extends MoveInsideCharacter { - keys = ['a', 'B']; - charToMatch = '{'; - includeSurrounding = true; -} - -@RegisterAction -class MoveICaret extends MoveInsideCharacter { - keys = ['i', '<']; - charToMatch = '<'; -} - -@RegisterAction -class MoveIClosingCaret extends MoveInsideCharacter { - keys = ['i', '>']; - charToMatch = '<'; -} - -@RegisterAction -export class MoveACaret extends MoveInsideCharacter { - keys = ['a', '<']; - charToMatch = '<'; - includeSurrounding = true; -} - -@RegisterAction -class MoveAClosingCaret extends MoveInsideCharacter { - keys = ['a', '>']; - charToMatch = '<'; - includeSurrounding = true; -} - -@RegisterAction -class MoveISquareBracket extends MoveInsideCharacter { - keys = ['i', '[']; - charToMatch = '['; -} - -@RegisterAction -class MoveIClosingSquareBraket extends MoveInsideCharacter { - keys = ['i', ']']; - charToMatch = '['; -} - -@RegisterAction -export class MoveASquareBracket extends MoveInsideCharacter { - keys = ['a', '[']; - charToMatch = '['; - includeSurrounding = true; -} - -@RegisterAction -class MoveAClosingSquareBracket extends MoveInsideCharacter { - keys = ['a', ']']; - charToMatch = '['; - includeSurrounding = true; -} - -export abstract class MoveQuoteMatch extends BaseMovement { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualBlock]; - protected charToMatch: string; - protected includeSurrounding = false; - - public async execAction(position: Position, vimState: VimState): Promise { - const text = TextEditor.getLineAt(position).text; - const quoteMatcher = new QuoteMatcher(this.charToMatch, text); - const start = quoteMatcher.findOpening(position.character); - const end = quoteMatcher.findClosing(start + 1); - - if (start === -1 || end === -1 || end === start || end < position.character) { - return { - start: position, - stop: position, - failed: true, - }; - } - - let startPos = new Position(position.line, start); - let endPos = new Position(position.line, end); - - if (!this.includeSurrounding) { - startPos = startPos.getRight(); - endPos = endPos.getLeft(); - } - - if (position.isBefore(startPos)) { - vimState.recordedState.operatorPositionDiff = startPos.subtract(position); - } - - return { - start: startPos, - stop: endPos, - }; - } - - public async execActionForOperator( - position: Position, - vimState: VimState - ): Promise { - const result = await this.execAction(position, vimState); - if (isIMovement(result)) { - if (result.failed) { - vimState.recordedState.hasRunOperator = false; - vimState.recordedState.actionsRun = []; - } else { - result.stop = result.stop.getRight(); - } - } - return result; - } -} - -@RegisterAction -class MoveInsideSingleQuotes extends MoveQuoteMatch { - keys = ['i', "'"]; - charToMatch = "'"; - includeSurrounding = false; -} - -@RegisterAction -export class MoveASingleQuotes extends MoveQuoteMatch { - keys = ['a', "'"]; - charToMatch = "'"; - includeSurrounding = true; -} - -@RegisterAction -class MoveInsideDoubleQuotes extends MoveQuoteMatch { - keys = ['i', '"']; - charToMatch = '"'; - includeSurrounding = false; -} - -@RegisterAction -export class MoveADoubleQuotes extends MoveQuoteMatch { - keys = ['a', '"']; - charToMatch = '"'; - includeSurrounding = true; -} - -@RegisterAction -class MoveInsideBacktick extends MoveQuoteMatch { - keys = ['i', '`']; - charToMatch = '`'; - includeSurrounding = false; -} - -@RegisterAction -export class MoveABacktick extends MoveQuoteMatch { - keys = ['a', '`']; - charToMatch = '`'; - includeSurrounding = true; -} - -@RegisterAction -class MoveToUnclosedRoundBracketBackward extends MoveToMatchingBracket { - keys = ['[', '(']; - - public async execAction(position: Position, vimState: VimState): Promise { - const failure = { start: position, stop: position, failed: true }; - const charToMatch = ')'; - const result = PairMatcher.nextPairedChar( - position.getLeftThroughLineBreaks(), - charToMatch, - false - ); - - if (!result) { - return failure; - } - return result; - } -} - -@RegisterAction -class MoveToUnclosedRoundBracketForward extends MoveToMatchingBracket { - keys = [']', ')']; - - public async execAction(position: Position, vimState: VimState): Promise { - const failure = { start: position, stop: position, failed: true }; - const charToMatch = '('; - const result = PairMatcher.nextPairedChar( - position.getRightThroughLineBreaks(), - charToMatch, - false - ); - - if (!result) { - return failure; - } - return result; - } -} - -@RegisterAction -class MoveToUnclosedCurlyBracketBackward extends MoveToMatchingBracket { - keys = ['[', '{']; - - public async execAction(position: Position, vimState: VimState): Promise { - const failure = { start: position, stop: position, failed: true }; - const charToMatch = '}'; - const result = PairMatcher.nextPairedChar( - position.getLeftThroughLineBreaks(), - charToMatch, - false - ); - - if (!result) { - return failure; - } - return result; - } -} - -@RegisterAction -class MoveToUnclosedCurlyBracketForward extends MoveToMatchingBracket { - keys = [']', '}']; - - public async execAction(position: Position, vimState: VimState): Promise { - const failure = { start: position, stop: position, failed: true }; - const charToMatch = '{'; - const result = PairMatcher.nextPairedChar( - position.getRightThroughLineBreaks(), - charToMatch, - false - ); - - if (!result) { - return failure; - } - return result; - } -} - -abstract class MoveTagMatch extends BaseMovement { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualBlock]; - protected includeTag = false; - - public async execAction(position: Position, vimState: VimState): Promise { - const editorText = TextEditor.getText(); - const offset = TextEditor.getOffsetAt(position); - const tagMatcher = new TagMatcher(editorText, offset); - const start = tagMatcher.findOpening(this.includeTag); - const end = tagMatcher.findClosing(this.includeTag); - - if (start === undefined || end === undefined) { - return { - start: position, - stop: position, - failed: true, - }; - } - - let startPosition = start ? TextEditor.getPositionAt(start) : position; - let endPosition = end ? TextEditor.getPositionAt(end) : position; - - if (position.isAfter(endPosition)) { - vimState.recordedState.transformations.push({ - type: 'moveCursor', - diff: endPosition.subtract(position), - }); - } else if (position.isBefore(startPosition)) { - vimState.recordedState.transformations.push({ - type: 'moveCursor', - diff: startPosition.subtract(position), - }); - } - if (start === end) { - if (vimState.recordedState.operator instanceof ChangeOperator) { - vimState.currentMode = ModeName.Insert; - } - return { - start: startPosition, - stop: startPosition, - failed: true, - }; - } - return { - start: startPosition, - stop: endPosition.getLeftThroughLineBreaks(true), - }; - } - - public async execActionForOperator( - position: Position, - vimState: VimState - ): Promise { - const result = await this.execAction(position, vimState); - if (isIMovement(result)) { - if (result.failed) { - vimState.recordedState.hasRunOperator = false; - vimState.recordedState.actionsRun = []; - } else { - result.stop = result.stop.getRight(); - } - } - return result; - } -} - -@RegisterAction -export class MoveInsideTag extends MoveTagMatch { - keys = ['i', 't']; - includeTag = false; -} - -@RegisterAction -export class MoveAroundTag extends MoveTagMatch { - keys = ['a', 't']; - includeTag = true; -} - -export class ArrowsInInsertMode extends BaseMovement { - modes = [ModeName.Insert]; - keys: string[]; - canBePrefixedWithCount = true; - - public async execAction(position: Position, vimState: VimState): Promise { - // we are in Insert Mode and arrow keys will clear all other actions except the first action, which enters Insert Mode. - // Please note the arrow key movement can be repeated while using `.` but it can't be repeated when using `` in Insert Mode. - vimState.recordedState.actionsRun = [ - vimState.recordedState.actionsRun.shift()!, - vimState.recordedState.actionsRun.pop()!, - ]; - let newPosition: Position = position; - - switch (this.keys[0]) { - case '': - newPosition = await new MoveUpArrow().execAction(position, vimState); - break; - case '': - newPosition = await new MoveDownArrow().execAction(position, vimState); - break; - case '': - newPosition = await new MoveLeftArrow().execAction(position, vimState); - break; - case '': - newPosition = await new MoveRightArrow().execAction(position, vimState); - break; - default: - break; - } - vimState.replaceState = new ReplaceState(newPosition); - return newPosition; - } -} - -@RegisterAction -class UpArrowInInsertMode extends ArrowsInInsertMode { - keys = ['']; -} - -@RegisterAction -class DownArrowInInsertMode extends ArrowsInInsertMode { - keys = ['']; -} - -@RegisterAction -class LeftArrowInInsertMode extends ArrowsInInsertMode { - keys = ['']; -} - -@RegisterAction -class RightArrowInInsertMode extends ArrowsInInsertMode { - keys = ['']; -} +import * as vscode from 'vscode'; +import { ModeName } from './../mode/mode'; +import { Position, PositionDiff } from './../common/motion/position'; +import { Configuration } from './../configuration/configuration'; +import { TextEditor, CursorMovePosition, CursorMoveByUnit } from './../textEditor'; +import { VimState } from './../mode/modeHandler'; +import { RegisterMode } from './../register/register'; +import { PairMatcher } from './../common/matching/matcher'; +import { ReplaceState } from './../state/replaceState'; +import { QuoteMatcher } from './../common/matching/quoteMatcher'; +import { TagMatcher } from './../common/matching/tagMatcher'; +import { RegisterAction } from './base'; +import { ChangeOperator } from './operator'; +import { BaseAction } from './base'; + +export function isIMovement(o: IMovement | Position): o is IMovement { + return (o as IMovement).start !== undefined && (o as IMovement).stop !== undefined; +} + +/** + * The result of a (more sophisticated) Movement. + */ +export interface IMovement { + start: Position; + stop: Position; + + /** + * Whether this motion succeeded. Some commands, like fx when 'x' can't be found, + * will not move the cursor. Furthermore, dfx won't delete *anything*, even though + * deleting to the current character would generally delete 1 character. + */ + failed?: boolean; + + diff?: PositionDiff; + + // It /so/ annoys me that I have to put this here. + registerMode?: RegisterMode; +} + +/** + * A movement is something like 'h', 'k', 'w', 'b', 'gg', etc. + */ +export abstract class BaseMovement extends BaseAction { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + + isMotion = true; + + canBePrefixedWithCount = false; + + /** + * Whether we should change lastRepeatableMovement in VimState. + */ + public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { + return false; + } + + /** + * Whether we should change desiredColumn in VimState. + */ + public doesntChangeDesiredColumn = false; + + /** + * This is for commands like $ which force the desired column to be at + * the end of even the longest line. + */ + public setsDesiredColumnToEOL = false; + + /** + * Run the movement a single time. + * + * Generally returns a new Position. If necessary, it can return an IMovement instead. + * Note: If returning an IMovement, make sure that repeated actions on a + * visual selection work. For example, V}} + */ + public async execAction(position: Position, vimState: VimState): Promise { + throw new Error('Not implemented!'); + } + + /** + * Run the movement in an operator context a single time. + * + * Some movements operate over different ranges when used for operators. + */ + public async execActionForOperator( + position: Position, + vimState: VimState + ): Promise { + return await this.execAction(position, vimState); + } + + /** + * Run a movement count times. + * + * count: the number prefix the user entered, or 0 if they didn't enter one. + */ + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + let recordedState = vimState.recordedState; + let result: Position | IMovement = new Position(0, 0); // bogus init to satisfy typechecker + + if (count < 1) { + count = 1; + } else if (count > 99999) { + count = 99999; + } + + for (let i = 0; i < count; i++) { + const firstIteration = i === 0; + const lastIteration = i === count - 1; + const temporaryResult = + recordedState.operator && lastIteration + ? await this.execActionForOperator(position, vimState) + : await this.execAction(position, vimState); + + if (temporaryResult instanceof Position) { + result = temporaryResult; + position = temporaryResult; + } else if (isIMovement(temporaryResult)) { + if (result instanceof Position) { + result = { + start: new Position(0, 0), + stop: new Position(0, 0), + failed: false, + }; + } + + result.failed = result.failed || temporaryResult.failed; + + if (firstIteration) { + (result as IMovement).start = temporaryResult.start; + } + + if (lastIteration) { + (result as IMovement).stop = temporaryResult.stop; + } else { + position = temporaryResult.stop.getRightThroughLineBreaks(); + } + + result.registerMode = temporaryResult.registerMode; + } + } + + return result; + } +} + +abstract class MoveByScreenLine extends BaseMovement { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + movementType: CursorMovePosition; + by: CursorMoveByUnit; + value: number = 1; + + public async execAction(position: Position, vimState: VimState): Promise { + await vscode.commands.executeCommand('cursorMove', { + to: this.movementType, + select: vimState.currentMode !== ModeName.Normal, + by: this.by, + value: this.value, + }); + + if (vimState.currentMode === ModeName.Normal) { + return Position.FromVSCodePosition(vimState.editor.selection.active); + } else { + /** + * cursorMove command is handling the selection for us. + * So we are not following our design principal (do no real movement inside an action) here. + */ + let start = Position.FromVSCodePosition(vimState.editor.selection.start); + let stop = Position.FromVSCodePosition(vimState.editor.selection.end); + let curPos = Position.FromVSCodePosition(vimState.editor.selection.active); + + // We want to swap the cursor start stop positions based on which direction we are moving, up or down + if (start.isEqual(curPos)) { + position = start; + [start, stop] = [stop, start]; + start = start.getLeft(); + } + + return { start, stop }; + } + } + + public async execActionForOperator(position: Position, vimState: VimState): Promise { + await vscode.commands.executeCommand('cursorMove', { + to: this.movementType, + select: true, + by: this.by, + value: this.value, + }); + + return { + start: Position.FromVSCodePosition(vimState.editor.selection.start), + stop: Position.FromVSCodePosition(vimState.editor.selection.end), + }; + } +} + +abstract class MoveByScreenLineMaintainDesiredColumn extends MoveByScreenLine { + doesntChangeDesiredColumn = true; + public async execAction(position: Position, vimState: VimState): Promise { + let prevDesiredColumn = vimState.desiredColumn; + let prevLine = vimState.editor.selection.active.line; + + await vscode.commands.executeCommand('cursorMove', { + to: this.movementType, + select: vimState.currentMode !== ModeName.Normal, + by: this.by, + value: this.value, + }); + + if (vimState.currentMode === ModeName.Normal) { + let returnedPos = Position.FromVSCodePosition(vimState.editor.selection.active); + if (prevLine !== returnedPos.line) { + returnedPos = returnedPos.setLocation(returnedPos.line, prevDesiredColumn); + } + return returnedPos; + } else { + /** + * cursorMove command is handling the selection for us. + * So we are not following our design principal (do no real movement inside an action) here. + */ + let start = Position.FromVSCodePosition(vimState.editor.selection.start); + let stop = Position.FromVSCodePosition(vimState.editor.selection.end); + let curPos = Position.FromVSCodePosition(vimState.editor.selection.active); + + // We want to swap the cursor start stop positions based on which direction we are moving, up or down + if (start.isEqual(curPos)) { + position = start; + [start, stop] = [stop, start]; + start = start.getLeft(); + } + + return { start, stop }; + } + } +} + +class MoveDownByScreenLineMaintainDesiredColumn extends MoveByScreenLineMaintainDesiredColumn { + movementType: CursorMovePosition = 'down'; + by: CursorMoveByUnit = 'wrappedLine'; + value = 1; +} + +class MoveDownFoldFix extends MoveByScreenLineMaintainDesiredColumn { + movementType: CursorMovePosition = 'down'; + by: CursorMoveByUnit = 'line'; + value = 1; + + public async execAction(position: Position, vimState: VimState): Promise { + if (position.line === TextEditor.getLineCount() - 1) { + return position; + } + let t: Position; + let count = 0; + const prevDesiredColumn = vimState.desiredColumn; + do { + t = await new MoveDownByScreenLine().execAction(position, vimState); + count += 1; + } while (t.line === position.line); + if (t.line > position.line + 1) { + return t; + } + while (count > 0) { + t = await new MoveUpByScreenLine().execAction(position, vimState); + count--; + } + vimState.desiredColumn = prevDesiredColumn; + return await position.getDown(vimState.desiredColumn); + } +} + +@RegisterAction +class MoveDown extends BaseMovement { + keys = ['j']; + doesntChangeDesiredColumn = true; + + public async execAction(position: Position, vimState: VimState): Promise { + if (Configuration.foldfix && vimState.currentMode !== ModeName.VisualBlock) { + return new MoveDownFoldFix().execAction(position, vimState); + } + return position.getDown(vimState.desiredColumn); + } + + public async execActionForOperator(position: Position, vimState: VimState): Promise { + vimState.currentRegisterMode = RegisterMode.LineWise; + return position.getDown(position.getLineEnd().character); + } +} + +@RegisterAction +class MoveDownArrow extends MoveDown { + keys = ['']; +} + +class MoveUpByScreenLineMaintainDesiredColumn extends MoveByScreenLineMaintainDesiredColumn { + movementType: CursorMovePosition = 'up'; + by: CursorMoveByUnit = 'wrappedLine'; + value = 1; +} + +@RegisterAction +class MoveUp extends BaseMovement { + keys = ['k']; + doesntChangeDesiredColumn = true; + + public async execAction(position: Position, vimState: VimState): Promise { + if (Configuration.foldfix && vimState.currentMode !== ModeName.VisualBlock) { + return new MoveUpFoldFix().execAction(position, vimState); + } + return position.getUp(vimState.desiredColumn); + } + + public async execActionForOperator(position: Position, vimState: VimState): Promise { + vimState.currentRegisterMode = RegisterMode.LineWise; + return position.getUp(position.getLineEnd().character); + } +} + +@RegisterAction +class MoveUpFoldFix extends MoveByScreenLineMaintainDesiredColumn { + movementType: CursorMovePosition = 'up'; + by: CursorMoveByUnit = 'line'; + value = 1; + + public async execAction(position: Position, vimState: VimState): Promise { + if (position.line === 0) { + return position; + } + let t: Position; + const prevDesiredColumn = vimState.desiredColumn; + let count = 0; + + do { + t = await new MoveUpByScreenLineMaintainDesiredColumn().execAction( + position, + vimState + ); + count += 1; + } while (t.line === position.line); + vimState.desiredColumn = prevDesiredColumn; + if (t.line < position.line - 1) { + return t; + } + while (count > 0) { + t = await new MoveDownByScreenLine().execAction(position, vimState); + count--; + } + vimState.desiredColumn = prevDesiredColumn; + return await position.getUp(vimState.desiredColumn); + } +} + +@RegisterAction +class MoveUpArrow extends MoveUp { + keys = ['']; +} + +@RegisterAction +class ArrowsInReplaceMode extends BaseMovement { + modes = [ModeName.Replace]; + keys = [[''], [''], [''], ['']]; + + public async execAction(position: Position, vimState: VimState): Promise { + let newPosition: Position = position; + + switch (this.keysPressed[0]) { + case '': + newPosition = await new MoveUpArrow().execAction(position, vimState); + break; + case '': + newPosition = await new MoveDownArrow().execAction(position, vimState); + break; + case '': + newPosition = await new MoveLeftArrow().execAction(position, vimState); + break; + case '': + newPosition = await new MoveRightArrow().execAction(position, vimState); + break; + default: + break; + } + vimState.replaceState = new ReplaceState(newPosition); + return newPosition; + } +} + +@RegisterAction +class UpArrowInReplaceMode extends ArrowsInReplaceMode { + keys = [['']]; +} + +@RegisterAction +class DownArrowInReplaceMode extends ArrowsInReplaceMode { + keys = [['']]; +} + +@RegisterAction +class LeftArrowInReplaceMode extends ArrowsInReplaceMode { + keys = [['']]; +} + +@RegisterAction +class RightArrowInReplaceMode extends ArrowsInReplaceMode { + keys = [['']]; +} + +@RegisterAction +class CommandNextSearchMatch extends BaseMovement { + keys = ['n']; + + public async execAction(position: Position, vimState: VimState): Promise { + const searchState = vimState.globalState.searchState; + + if (!searchState || searchState.searchString === '') { + return position; + } + // Turn one of the highlighting flags back on (turned off with :nohl) + vimState.globalState.hl = true; + + if (vimState.cursorPosition.getRight().isEqual(vimState.cursorPosition.getLineEnd())) { + return searchState.getNextSearchMatchPosition(vimState.cursorPosition.getRight()).pos; + } + + // Turn one of the highlighting flags back on (turned off with :nohl) + + return searchState.getNextSearchMatchPosition(vimState.cursorPosition).pos; + } +} + +@RegisterAction +class CommandPreviousSearchMatch extends BaseMovement { + keys = ['N']; + + public async execAction(position: Position, vimState: VimState): Promise { + const searchState = vimState.globalState.searchState; + + if (!searchState || searchState.searchString === '') { + return position; + } + + // Turn one of the highlighting flags back on (turned off with :nohl) + vimState.globalState.hl = true; + + return searchState.getNextSearchMatchPosition(vimState.cursorPosition, -1).pos; + } +} + +@RegisterAction +export class MarkMovementBOL extends BaseMovement { + keys = ["'", '']; + + public async execAction(position: Position, vimState: VimState): Promise { + const markName = this.keysPressed[1]; + const mark = vimState.historyTracker.getMark(markName); + + vimState.currentRegisterMode = RegisterMode.LineWise; + + return mark.position.getFirstLineNonBlankChar(); + } +} + +@RegisterAction +export class MarkMovement extends BaseMovement { + keys = ['`', '']; + + public async execAction(position: Position, vimState: VimState): Promise { + const markName = this.keysPressed[1]; + const mark = vimState.historyTracker.getMark(markName); + + return mark.position; + } +} + +@RegisterAction +export class MoveLeft extends BaseMovement { + keys = ['h']; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getLeft(); + } +} + +@RegisterAction +class MoveLeftArrow extends MoveLeft { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + keys = ['']; +} + +@RegisterAction +class BackSpaceInNormalMode extends BaseMovement { + modes = [ModeName.Normal]; + keys = ['']; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getLeftThroughLineBreaks(); + } +} + +@RegisterAction +class MoveRight extends BaseMovement { + keys = ['l']; + + public async execAction(position: Position, vimState: VimState): Promise { + return new Position(position.line, position.character + 1); + } +} + +@RegisterAction +class MoveRightArrow extends MoveRight { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + keys = ['']; +} + +@RegisterAction +class MoveRightWithSpace extends BaseMovement { + keys = [' ']; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getRightThroughLineBreaks(); + } +} + +@RegisterAction +class MoveDownNonBlank extends BaseMovement { + keys = ['+']; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + return position.getDownByCount(Math.max(count, 1)).getFirstLineNonBlankChar(); + } +} + +@RegisterAction +class MoveUpNonBlank extends BaseMovement { + keys = ['-']; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + return position.getUpByCount(Math.max(count, 1)).getFirstLineNonBlankChar(); + } +} + +@RegisterAction +class MoveDownUnderscore extends BaseMovement { + keys = ['_']; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + return position.getDownByCount(Math.max(count - 1, 0)).getFirstLineNonBlankChar(); + } +} + +@RegisterAction +class MoveToColumn extends BaseMovement { + keys = ['|']; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + return new Position(position.line, Math.max(0, count - 1)); + } +} + +@RegisterAction +class MoveFindForward extends BaseMovement { + keys = ['f', '']; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + count = count || 1; + const toFind = this.keysPressed[1]; + let result = position.findForwards(toFind, count); + + if (!result) { + return { start: position, stop: position, failed: true }; + } + + if (vimState.recordedState.operator) { + result = result.getRight(); + } + + return result; + } + + public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { + return !vimState.recordedState.operator || !(isIMovement(result) && result.failed); + } +} + +@RegisterAction +class MoveFindBackward extends BaseMovement { + keys = ['F', '']; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + count = count || 1; + const toFind = this.keysPressed[1]; + let result = position.findBackwards(toFind, count); + + if (!result) { + return { start: position, stop: position, failed: true }; + } + + return result; + } + + public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { + return !vimState.recordedState.operator || !(isIMovement(result) && result.failed); + } +} + +@RegisterAction +class MoveTilForward extends BaseMovement { + keys = ['t', '']; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + count = count || 1; + const toFind = this.keysPressed[1]; + let result = position.tilForwards(toFind, count); + + if (!result) { + return { start: position, stop: position, failed: true }; + } + + if (vimState.recordedState.operator) { + result = result.getRight(); + } + + return result; + } + + public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { + return !vimState.recordedState.operator || !(isIMovement(result) && result.failed); + } +} + +@RegisterAction +class MoveTilBackward extends BaseMovement { + keys = ['T', '']; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + count = count || 1; + const toFind = this.keysPressed[1]; + let result = position.tilBackwards(toFind, count); + + if (!result) { + return { start: position, stop: position, failed: true }; + } + + return result; + } + + public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { + return !vimState.recordedState.operator || !(isIMovement(result) && result.failed); + } +} + +@RegisterAction +class MoveRepeat extends BaseMovement { + keys = [';']; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + const movement = VimState.lastRepeatableMovement; + if (movement) { + const result = await movement.execActionWithCount(position, vimState, count); + /** + * For t and T commands vim executes ; as 2; + * This way the cursor will get to the next instance of + */ + if (result instanceof Position && position.isEqual(result) && count <= 1) { + return await movement.execActionWithCount(position, vimState, 2); + } + return result; + } + return position; + } +} + +@RegisterAction +class MoveRepeatReversed extends BaseMovement { + keys = [',']; + static reverseMotionMapping: Map BaseMovement> = new Map([ + [MoveFindForward, () => new MoveFindBackward()], + [MoveFindBackward, () => new MoveFindForward()], + [MoveTilForward, () => new MoveTilBackward()], + [MoveTilBackward, () => new MoveTilForward()], + ]); + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + const movement = VimState.lastRepeatableMovement; + if (movement) { + const reverse = MoveRepeatReversed.reverseMotionMapping.get(movement.constructor)!(); + reverse.keysPressed = [(reverse.keys as string[])[0], movement.keysPressed[1]]; + + let result = await reverse.execActionWithCount(position, vimState, count); + // For t and T commands vim executes ; as 2; + if (result instanceof Position && position.isEqual(result) && count <= 1) { + result = await reverse.execActionWithCount(position, vimState, 2); + } + return result; + } + return position; + } +} + +@RegisterAction +class MoveLineEnd extends BaseMovement { + keys = [['$'], [''], ['']]; + setsDesiredColumnToEOL = true; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + return position.getDownByCount(Math.max(count - 1, 0)).getLineEnd(); + } +} + +@RegisterAction +class MoveLineBegin extends BaseMovement { + keys = [['0'], [''], ['']]; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getLineBegin(); + } + + public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { + return super.doesActionApply(vimState, keysPressed) && vimState.recordedState.count === 0; + } + + public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { + return super.couldActionApply(vimState, keysPressed) && vimState.recordedState.count === 0; + } +} + +@RegisterAction +class MoveScreenLineBegin extends MoveByScreenLine { + keys = ['g', '0']; + movementType: CursorMovePosition = 'wrappedLineStart'; +} + +@RegisterAction +class MoveScreenNonBlank extends MoveByScreenLine { + keys = ['g', '^']; + movementType: CursorMovePosition = 'wrappedLineFirstNonWhitespaceCharacter'; +} + +@RegisterAction +class MoveScreenLineEnd extends MoveByScreenLine { + keys = ['g', '$']; + movementType: CursorMovePosition = 'wrappedLineEnd'; +} + +@RegisterAction +class MoveScreenLineEndNonBlank extends MoveByScreenLine { + keys = ['g', '_']; + movementType: CursorMovePosition = 'wrappedLineLastNonWhitespaceCharacter'; + canBePrefixedWithCount = true; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + count = count || 1; + const pos = await this.execAction(position, vimState); + const newPos: Position | IMovement = pos as Position; + + // If in visual, return a selection + if (pos instanceof Position) { + return pos.getDownByCount(count - 1); + } else if (isIMovement(pos)) { + return { start: pos.start, stop: pos.stop.getDownByCount(count - 1).getLeft() }; + } + + return newPos.getDownByCount(count - 1); + } +} + +@RegisterAction +class MoveScreenLineCenter extends MoveByScreenLine { + keys = ['g', 'm']; + movementType: CursorMovePosition = 'wrappedLineColumnCenter'; +} + +@RegisterAction +export class MoveUpByScreenLine extends MoveByScreenLine { + modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual]; + keys = [['g', 'k'], ['g', '']]; + movementType: CursorMovePosition = 'up'; + by: CursorMoveByUnit = 'wrappedLine'; + value = 1; +} + +@RegisterAction +class MoveDownByScreenLine extends MoveByScreenLine { + modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual]; + keys = [['g', 'j'], ['g', '']]; + movementType: CursorMovePosition = 'down'; + by: CursorMoveByUnit = 'wrappedLine'; + value = 1; +} + +// Because we can't support moving by screen line when in visualLine mode, +// we change to moving by regular line in visualLine mode. We can't move by +// screen line is that our ranges only support a start and stop attribute, +// and moving by screen line just snaps us back to the original position. +// Check PR #1600 for discussion. +@RegisterAction +class MoveUpByScreenLineVisualLine extends MoveByScreenLine { + modes = [ModeName.VisualLine]; + keys = [['g', 'k'], ['g', '']]; + movementType: CursorMovePosition = 'up'; + by: CursorMoveByUnit = 'line'; + value = 1; +} + +@RegisterAction +class MoveDownByScreenLineVisualLine extends MoveByScreenLine { + modes = [ModeName.VisualLine]; + keys = [['g', 'j'], ['g', '']]; + movementType: CursorMovePosition = 'down'; + by: CursorMoveByUnit = 'line'; + value = 1; +} + +@RegisterAction +class MoveScreenToRight extends MoveByScreenLine { + modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ['z', 'h']; + movementType: CursorMovePosition = 'right'; + by: CursorMoveByUnit = 'character'; + value = 1; +} + +@RegisterAction +class MoveScreenToLeft extends MoveByScreenLine { + modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ['z', 'l']; + movementType: CursorMovePosition = 'left'; + by: CursorMoveByUnit = 'character'; + value = 1; +} + +@RegisterAction +class MoveScreenToRightHalf extends MoveByScreenLine { + modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ['z', 'H']; + movementType: CursorMovePosition = 'right'; + by: CursorMoveByUnit = 'halfLine'; + value = 1; +} + +@RegisterAction +class MoveScreenToLeftHalf extends MoveByScreenLine { + modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ['z', 'L']; + movementType: CursorMovePosition = 'left'; + by: CursorMoveByUnit = 'halfLine'; + value = 1; +} + +@RegisterAction +class MoveToLineFromViewPortTop extends MoveByScreenLine { + keys = ['H']; + movementType: CursorMovePosition = 'viewPortTop'; + by: CursorMoveByUnit = 'line'; + value = 1; + canBePrefixedWithCount = true; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + this.value = count < 1 ? 1 : count; + return await this.execAction(position, vimState); + } +} + +@RegisterAction +class MoveToLineFromViewPortBottom extends MoveByScreenLine { + keys = ['L']; + movementType: CursorMovePosition = 'viewPortBottom'; + by: CursorMoveByUnit = 'line'; + value = 1; + canBePrefixedWithCount = true; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + this.value = count < 1 ? 1 : count; + return await this.execAction(position, vimState); + } +} + +@RegisterAction +class MoveToMiddleLineInViewPort extends MoveByScreenLine { + keys = ['M']; + movementType: CursorMovePosition = 'viewPortCenter'; + by: CursorMoveByUnit = 'line'; +} + +@RegisterAction +class MoveNonBlank extends BaseMovement { + keys = ['^']; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getFirstLineNonBlankChar(); + } +} + +@RegisterAction +class MoveNextLineNonBlank extends BaseMovement { + keys = ['\n']; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + vimState.currentRegisterMode = RegisterMode.LineWise; + + // Count === 0 if just pressing enter in normal mode, need to still go down 1 line + if (count === 0) { + count++; + } + + return position.getDownByCount(count).getFirstLineNonBlankChar(); + } +} + +@RegisterAction +class MoveNonBlankFirst extends BaseMovement { + keys = ['g', 'g']; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + if (count === 0) { + return position.getDocumentBegin().getFirstLineNonBlankChar(); + } + + return new Position(count - 1, 0).getFirstLineNonBlankChar(); + } +} + +@RegisterAction +class MoveNonBlankLast extends BaseMovement { + keys = ['G']; + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + let stop: Position; + + if (count === 0) { + stop = new Position(TextEditor.getLineCount() - 1, 0); + } else { + stop = new Position(Math.min(count, TextEditor.getLineCount()) - 1, 0); + } + + return { + start: vimState.cursorStartPosition, + stop: stop, + registerMode: RegisterMode.LineWise, + }; + } +} + +@RegisterAction +export class MoveWordBegin extends BaseMovement { + keys = ['w']; + + public async execAction( + position: Position, + vimState: VimState, + isLastIteration: boolean = false + ): Promise { + if (isLastIteration && vimState.recordedState.operator instanceof ChangeOperator) { + if (TextEditor.getLineAt(position).text.length < 1) { + return position; + } + + const line = TextEditor.getLineAt(position).text; + const char = line[position.character]; + + /* + From the Vim manual: + + Special case: "cw" and "cW" are treated like "ce" and "cE" if the cursor is + on a non-blank. This is because "cw" is interpreted as change-word, and a + word does not include the following white space. + */ + + if (' \t'.indexOf(char) >= 0) { + return position.getWordRight(); + } else { + return position.getCurrentWordEnd(true).getRight(); + } + } else { + return position.getWordRight(); + } + } + + public async execActionForOperator(position: Position, vimState: VimState): Promise { + const result = await this.execAction(position, vimState, true); + + /* + From the Vim documentation: + + Another special case: When using the "w" motion in combination with an + operator and the last word moved over is at the end of a line, the end of + that word becomes the end of the operated text, not the first word in the + next line. + */ + + if ( + result.line > position.line + 1 || + (result.line === position.line + 1 && result.isFirstWordOfLine()) + ) { + return position.getLineEnd(); + } + + if (result.isLineEnd()) { + return new Position(result.line, result.character + 1); + } + + return result; + } +} + +@RegisterAction +class MoveFullWordBegin extends BaseMovement { + keys = ['W']; + + public async execAction(position: Position, vimState: VimState): Promise { + if (vimState.recordedState.operator instanceof ChangeOperator) { + // TODO use execForOperator? Or maybe dont? + + // See note for w + return position.getCurrentBigWordEnd().getRight(); + } else { + return position.getBigWordRight(); + } + } +} + +@RegisterAction +class MoveWordEnd extends BaseMovement { + keys = ['e']; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getCurrentWordEnd(); + } + + public async execActionForOperator(position: Position, vimState: VimState): Promise { + let end = position.getCurrentWordEnd(); + + return new Position(end.line, end.character + 1); + } +} + +@RegisterAction +class MoveFullWordEnd extends BaseMovement { + keys = ['E']; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getCurrentBigWordEnd(); + } + + public async execActionForOperator(position: Position, vimState: VimState): Promise { + return position.getCurrentBigWordEnd().getRight(); + } +} + +@RegisterAction +class MoveLastWordEnd extends BaseMovement { + keys = ['g', 'e']; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getLastWordEnd(); + } +} + +@RegisterAction +class MoveLastFullWordEnd extends BaseMovement { + keys = ['g', 'E']; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getLastBigWordEnd(); + } +} + +@RegisterAction +class MoveBeginningWord extends BaseMovement { + keys = ['b']; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getWordLeft(); + } +} + +@RegisterAction +class MoveBeginningFullWord extends BaseMovement { + keys = ['B']; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getBigWordLeft(); + } +} + +@RegisterAction +class MovePreviousSentenceBegin extends BaseMovement { + keys = ['(']; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getSentenceBegin({ forward: false }); + } +} + +@RegisterAction +class MoveNextSentenceBegin extends BaseMovement { + keys = [')']; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getSentenceBegin({ forward: true }); + } +} + +@RegisterAction +class MoveParagraphEnd extends BaseMovement { + keys = ['}']; + + public async execAction(position: Position, vimState: VimState): Promise { + const isLineWise = + position.isLineBeginning() && + vimState.currentMode === ModeName.Normal && + vimState.recordedState.operator; + let paragraphEnd = position.getCurrentParagraphEnd(); + vimState.currentRegisterMode = isLineWise + ? RegisterMode.LineWise + : RegisterMode.FigureItOutFromCurrentMode; + return isLineWise ? paragraphEnd.getLeftThroughLineBreaks(true) : paragraphEnd; + } +} + +@RegisterAction +class MoveParagraphBegin extends BaseMovement { + keys = ['{']; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getCurrentParagraphBeginning(); + } +} + +abstract class MoveSectionBoundary extends BaseMovement { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + boundary: string; + forward: boolean; + + public async execAction(position: Position, vimState: VimState): Promise { + return position.getSectionBoundary({ + forward: this.forward, + boundary: this.boundary, + }); + } +} + +@RegisterAction +class MoveNextSectionBegin extends MoveSectionBoundary { + keys = [']', ']']; + boundary = '{'; + forward = true; +} + +@RegisterAction +class MoveNextSectionEnd extends MoveSectionBoundary { + keys = [']', '[']; + boundary = '}'; + forward = true; +} + +@RegisterAction +class MovePreviousSectionBegin extends MoveSectionBoundary { + keys = ['[', '[']; + boundary = '{'; + forward = false; +} + +@RegisterAction +class MovePreviousSectionEnd extends MoveSectionBoundary { + keys = ['[', ']']; + boundary = '}'; + forward = false; +} + +@RegisterAction +class MoveToMatchingBracket extends BaseMovement { + keys = ['%']; + + public async execAction(position: Position, vimState: VimState): Promise { + position = position.getLeftIfEOL(); + + const text = TextEditor.getLineAt(position).text; + const charToMatch = text[position.character]; + const toFind = PairMatcher.pairings[charToMatch]; + const failure = { start: position, stop: position, failed: true }; + + if (!toFind || !toFind.matchesWithPercentageMotion) { + // If we're not on a match, go right until we find a + // pairable character or hit the end of line. + + for (let i = position.character; i < text.length; i++) { + if (PairMatcher.pairings[text[i]]) { + // We found an opening char, now move to the matching closing char + const openPosition = new Position(position.line, i); + return PairMatcher.nextPairedChar(openPosition, text[i], true) || failure; + } + } + + return failure; + } + + return PairMatcher.nextPairedChar(position, charToMatch, true) || failure; + } + + public async execActionForOperator( + position: Position, + vimState: VimState + ): Promise { + const result = await this.execAction(position, vimState); + + if (isIMovement(result)) { + if (result.failed) { + return result; + } else { + throw new Error('Did not ever handle this case!'); + } + } + + if (position.compareTo(result) > 0) { + return { + start: result, + stop: position.getRight(), + }; + } else { + return result.getRight(); + } + } + + public async execActionWithCount( + position: Position, + vimState: VimState, + count: number + ): Promise { + // % has a special mode that lets you use it to jump to a percentage of the file + // However, some other bracket motions inherit from this so only do this behavior for % explicitly + if (Object.getPrototypeOf(this) === MoveToMatchingBracket.prototype) { + if (count === 0) { + if (vimState.recordedState.operator) { + return this.execActionForOperator(position, vimState); + } else { + return this.execAction(position, vimState); + } + } + + // Check to make sure this is a valid percentage + if (count < 0 || count > 100) { + return { start: position, stop: position, failed: true }; + } + + const targetLine = Math.round(count * TextEditor.getLineCount() / 100); + return new Position(targetLine - 1, 0).getFirstLineNonBlankChar(); + } else { + return super.execActionWithCount(position, vimState, count); + } + } +} + +export abstract class MoveInsideCharacter extends BaseMovement { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + protected charToMatch: string; + protected includeSurrounding = false; + + public async execAction(position: Position, vimState: VimState): Promise { + const failure = { start: position, stop: position, failed: true }; + const text = TextEditor.getLineAt(position).text; + const closingChar = PairMatcher.pairings[this.charToMatch].match; + const closedMatch = text[position.character] === closingChar; + + // First, search backwards for the opening character of the sequence + let startPos = PairMatcher.nextPairedChar(position, closingChar, closedMatch); + if (startPos === undefined) { + return failure; + } + + let startPlusOne: Position; + + if (startPos.isAfterOrEqual(startPos.getLineEnd().getLeft())) { + startPlusOne = new Position(startPos.line + 1, 0); + } else { + startPlusOne = new Position(startPos.line, startPos.character + 1); + } + + let endPos = PairMatcher.nextPairedChar(startPlusOne, this.charToMatch, false); + if (endPos === undefined) { + return failure; + } + + if (this.includeSurrounding) { + if (vimState.currentMode !== ModeName.Visual) { + endPos = new Position(endPos.line, endPos.character + 1); + } + } else { + startPos = startPlusOne; + if (vimState.currentMode === ModeName.Visual) { + endPos = endPos.getLeftThroughLineBreaks(); + } + } + + // If the closing character is the first on the line, don't swallow it. + if (!this.includeSurrounding) { + if (endPos.getLeft().isInLeadingWhitespace()) { + endPos = endPos.getLineBegin(); + } + } + + if (position.isBefore(startPos)) { + vimState.recordedState.operatorPositionDiff = startPos.subtract(position); + } + + return { + start: startPos, + stop: endPos, + diff: new PositionDiff(0, startPos === position ? 1 : 0), + }; + } + + public async execActionForOperator( + position: Position, + vimState: VimState + ): Promise { + const result = await this.execAction(position, vimState); + if (isIMovement(result)) { + if (result.failed) { + vimState.recordedState.hasRunOperator = false; + vimState.recordedState.actionsRun = []; + } + } + return result; + } +} + +@RegisterAction +class MoveIParentheses extends MoveInsideCharacter { + keys = ['i', '(']; + charToMatch = '('; +} + +@RegisterAction +class MoveIClosingParentheses extends MoveInsideCharacter { + keys = ['i', ')']; + charToMatch = '('; +} + +@RegisterAction +class MoveIClosingParenthesesBlock extends MoveInsideCharacter { + keys = ['i', 'b']; + charToMatch = '('; +} + +@RegisterAction +export class MoveAParentheses extends MoveInsideCharacter { + keys = ['a', '(']; + charToMatch = '('; + includeSurrounding = true; +} + +@RegisterAction +class MoveAClosingParentheses extends MoveInsideCharacter { + keys = ['a', ')']; + charToMatch = '('; + includeSurrounding = true; +} + +@RegisterAction +class MoveAParenthesesBlock extends MoveInsideCharacter { + keys = ['a', 'b']; + charToMatch = '('; + includeSurrounding = true; +} + +@RegisterAction +class MoveICurlyBrace extends MoveInsideCharacter { + keys = ['i', '{']; + charToMatch = '{'; +} + +@RegisterAction +class MoveIClosingCurlyBrace extends MoveInsideCharacter { + keys = ['i', '}']; + charToMatch = '{'; +} + +@RegisterAction +class MoveIClosingCurlyBraceBlock extends MoveInsideCharacter { + keys = ['i', 'B']; + charToMatch = '{'; +} + +@RegisterAction +export class MoveACurlyBrace extends MoveInsideCharacter { + keys = ['a', '{']; + charToMatch = '{'; + includeSurrounding = true; +} + +@RegisterAction +export class MoveAClosingCurlyBrace extends MoveInsideCharacter { + keys = ['a', '}']; + charToMatch = '{'; + includeSurrounding = true; +} + +@RegisterAction +class MoveAClosingCurlyBraceBlock extends MoveInsideCharacter { + keys = ['a', 'B']; + charToMatch = '{'; + includeSurrounding = true; +} + +@RegisterAction +class MoveICaret extends MoveInsideCharacter { + keys = ['i', '<']; + charToMatch = '<'; +} + +@RegisterAction +class MoveIClosingCaret extends MoveInsideCharacter { + keys = ['i', '>']; + charToMatch = '<'; +} + +@RegisterAction +export class MoveACaret extends MoveInsideCharacter { + keys = ['a', '<']; + charToMatch = '<'; + includeSurrounding = true; +} + +@RegisterAction +class MoveAClosingCaret extends MoveInsideCharacter { + keys = ['a', '>']; + charToMatch = '<'; + includeSurrounding = true; +} + +@RegisterAction +class MoveISquareBracket extends MoveInsideCharacter { + keys = ['i', '[']; + charToMatch = '['; +} + +@RegisterAction +class MoveIClosingSquareBraket extends MoveInsideCharacter { + keys = ['i', ']']; + charToMatch = '['; +} + +@RegisterAction +export class MoveASquareBracket extends MoveInsideCharacter { + keys = ['a', '[']; + charToMatch = '['; + includeSurrounding = true; +} + +@RegisterAction +class MoveAClosingSquareBracket extends MoveInsideCharacter { + keys = ['a', ']']; + charToMatch = '['; + includeSurrounding = true; +} + +export abstract class MoveQuoteMatch extends BaseMovement { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualBlock]; + protected charToMatch: string; + protected includeSurrounding = false; + + public async execAction(position: Position, vimState: VimState): Promise { + const text = TextEditor.getLineAt(position).text; + const quoteMatcher = new QuoteMatcher(this.charToMatch, text); + const start = quoteMatcher.findOpening(position.character); + const end = quoteMatcher.findClosing(start + 1); + + if (start === -1 || end === -1 || end === start || end < position.character) { + return { + start: position, + stop: position, + failed: true, + }; + } + + let startPos = new Position(position.line, start); + let endPos = new Position(position.line, end); + + if (!this.includeSurrounding) { + startPos = startPos.getRight(); + endPos = endPos.getLeft(); + } + + if (position.isBefore(startPos)) { + vimState.recordedState.operatorPositionDiff = startPos.subtract(position); + } + + return { + start: startPos, + stop: endPos, + }; + } + + public async execActionForOperator( + position: Position, + vimState: VimState + ): Promise { + const result = await this.execAction(position, vimState); + if (isIMovement(result)) { + if (result.failed) { + vimState.recordedState.hasRunOperator = false; + vimState.recordedState.actionsRun = []; + } else { + result.stop = result.stop.getRight(); + } + } + return result; + } +} + +@RegisterAction +class MoveInsideSingleQuotes extends MoveQuoteMatch { + keys = ['i', "'"]; + charToMatch = "'"; + includeSurrounding = false; +} + +@RegisterAction +export class MoveASingleQuotes extends MoveQuoteMatch { + keys = ['a', "'"]; + charToMatch = "'"; + includeSurrounding = true; +} + +@RegisterAction +class MoveInsideDoubleQuotes extends MoveQuoteMatch { + keys = ['i', '"']; + charToMatch = '"'; + includeSurrounding = false; +} + +@RegisterAction +export class MoveADoubleQuotes extends MoveQuoteMatch { + keys = ['a', '"']; + charToMatch = '"'; + includeSurrounding = true; +} + +@RegisterAction +class MoveInsideBacktick extends MoveQuoteMatch { + keys = ['i', '`']; + charToMatch = '`'; + includeSurrounding = false; +} + +@RegisterAction +export class MoveABacktick extends MoveQuoteMatch { + keys = ['a', '`']; + charToMatch = '`'; + includeSurrounding = true; +} + +@RegisterAction +class MoveToUnclosedRoundBracketBackward extends MoveToMatchingBracket { + keys = ['[', '(']; + + public async execAction(position: Position, vimState: VimState): Promise { + const failure = { start: position, stop: position, failed: true }; + const charToMatch = ')'; + const result = PairMatcher.nextPairedChar( + position.getLeftThroughLineBreaks(), + charToMatch, + false + ); + + if (!result) { + return failure; + } + return result; + } +} + +@RegisterAction +class MoveToUnclosedRoundBracketForward extends MoveToMatchingBracket { + keys = [']', ')']; + + public async execAction(position: Position, vimState: VimState): Promise { + const failure = { start: position, stop: position, failed: true }; + const charToMatch = '('; + const result = PairMatcher.nextPairedChar( + position.getRightThroughLineBreaks(), + charToMatch, + false + ); + + if (!result) { + return failure; + } + return result; + } +} + +@RegisterAction +class MoveToUnclosedCurlyBracketBackward extends MoveToMatchingBracket { + keys = ['[', '{']; + + public async execAction(position: Position, vimState: VimState): Promise { + const failure = { start: position, stop: position, failed: true }; + const charToMatch = '}'; + const result = PairMatcher.nextPairedChar( + position.getLeftThroughLineBreaks(), + charToMatch, + false + ); + + if (!result) { + return failure; + } + return result; + } +} + +@RegisterAction +class MoveToUnclosedCurlyBracketForward extends MoveToMatchingBracket { + keys = [']', '}']; + + public async execAction(position: Position, vimState: VimState): Promise { + const failure = { start: position, stop: position, failed: true }; + const charToMatch = '{'; + const result = PairMatcher.nextPairedChar( + position.getRightThroughLineBreaks(), + charToMatch, + false + ); + + if (!result) { + return failure; + } + return result; + } +} + +abstract class MoveTagMatch extends BaseMovement { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualBlock]; + protected includeTag = false; + + public async execAction(position: Position, vimState: VimState): Promise { + const editorText = TextEditor.getText(); + const offset = TextEditor.getOffsetAt(position); + const tagMatcher = new TagMatcher(editorText, offset); + const start = tagMatcher.findOpening(this.includeTag); + const end = tagMatcher.findClosing(this.includeTag); + + if (start === undefined || end === undefined) { + return { + start: position, + stop: position, + failed: true, + }; + } + + let startPosition = start ? TextEditor.getPositionAt(start) : position; + let endPosition = end ? TextEditor.getPositionAt(end) : position; + + if (position.isAfter(endPosition)) { + vimState.recordedState.transformations.push({ + type: 'moveCursor', + diff: endPosition.subtract(position), + }); + } else if (position.isBefore(startPosition)) { + vimState.recordedState.transformations.push({ + type: 'moveCursor', + diff: startPosition.subtract(position), + }); + } + if (start === end) { + if (vimState.recordedState.operator instanceof ChangeOperator) { + vimState.currentMode = ModeName.Insert; + } + return { + start: startPosition, + stop: startPosition, + failed: true, + }; + } + return { + start: startPosition, + stop: endPosition.getLeftThroughLineBreaks(true), + }; + } + + public async execActionForOperator( + position: Position, + vimState: VimState + ): Promise { + const result = await this.execAction(position, vimState); + if (isIMovement(result)) { + if (result.failed) { + vimState.recordedState.hasRunOperator = false; + vimState.recordedState.actionsRun = []; + } else { + result.stop = result.stop.getRight(); + } + } + return result; + } +} + +@RegisterAction +export class MoveInsideTag extends MoveTagMatch { + keys = ['i', 't']; + includeTag = false; +} + +@RegisterAction +export class MoveAroundTag extends MoveTagMatch { + keys = ['a', 't']; + includeTag = true; +} + +export class ArrowsInInsertMode extends BaseMovement { + modes = [ModeName.Insert]; + keys: string[]; + canBePrefixedWithCount = true; + + public async execAction(position: Position, vimState: VimState): Promise { + // we are in Insert Mode and arrow keys will clear all other actions except the first action, which enters Insert Mode. + // Please note the arrow key movement can be repeated while using `.` but it can't be repeated when using `` in Insert Mode. + vimState.recordedState.actionsRun = [ + vimState.recordedState.actionsRun.shift()!, + vimState.recordedState.actionsRun.pop()!, + ]; + let newPosition: Position = position; + + switch (this.keys[0]) { + case '': + newPosition = await new MoveUpArrow().execAction(position, vimState); + break; + case '': + newPosition = await new MoveDownArrow().execAction(position, vimState); + break; + case '': + newPosition = await new MoveLeftArrow().execAction(position, vimState); + break; + case '': + newPosition = await new MoveRightArrow().execAction(position, vimState); + break; + default: + break; + } + vimState.replaceState = new ReplaceState(newPosition); + return newPosition; + } +} + +@RegisterAction +class UpArrowInInsertMode extends ArrowsInInsertMode { + keys = ['']; +} + +@RegisterAction +class DownArrowInInsertMode extends ArrowsInInsertMode { + keys = ['']; +} + +@RegisterAction +class LeftArrowInInsertMode extends ArrowsInInsertMode { + keys = ['']; +} + +@RegisterAction +class RightArrowInInsertMode extends ArrowsInInsertMode { + keys = ['']; +} diff --git a/src/common/matching/matcher.ts b/src/common/matching/matcher.ts index 2d465505280..e6a756d72dd 100644 --- a/src/common/matching/matcher.ts +++ b/src/common/matching/matcher.ts @@ -1,208 +1,208 @@ -import { Position, PositionDiff } from './../motion/position'; -import { TextEditor } from './../../textEditor'; -import * as vscode from 'vscode'; - -function escapeRegExpCharacters(value: string): string { - return value.replace(/[\-\\\{\}\*\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&'); -} - -let toReversedString = (function() { - function reverse(str: string): string { - let reversedStr = ''; - for (let i = str.length - 1; i >= 0; i--) { - reversedStr += str.charAt(i); - } - return reversedStr; - } - - let lastInput: string = ''; - let lastOutput: string = ''; - return function toReversedString(str: string): string { - if (lastInput !== str) { - lastInput = str; - lastOutput = reverse(lastInput); - } - return lastOutput; - }; -})(); - -/** - * PairMatcher finds the position matching the given character, respecting nested - * instances of the pair. - */ -export class PairMatcher { - static pairings: { - [key: string]: { - match: string; - nextMatchIsForward: boolean; - directionLess?: boolean; - matchesWithPercentageMotion?: boolean; - }; - } = { - '(': { match: ')', nextMatchIsForward: true, matchesWithPercentageMotion: true }, - '{': { match: '}', nextMatchIsForward: true, matchesWithPercentageMotion: true }, - '[': { match: ']', nextMatchIsForward: true, matchesWithPercentageMotion: true }, - ')': { match: '(', nextMatchIsForward: false, matchesWithPercentageMotion: true }, - '}': { match: '{', nextMatchIsForward: false, matchesWithPercentageMotion: true }, - ']': { match: '[', nextMatchIsForward: false, matchesWithPercentageMotion: true }, - // These characters can't be used for "%"-based matching, but are still - // useful for text objects. - '<': { match: '>', nextMatchIsForward: true }, - '>': { match: '<', nextMatchIsForward: false }, - // These are useful for deleting closing and opening quotes, but don't seem to negatively - // affect how text objects such as `ci"` work, which was my worry. - '"': { match: '"', nextMatchIsForward: false, directionLess: true }, - "'": { match: "'", nextMatchIsForward: false, directionLess: true }, - '`': { match: '`', nextMatchIsForward: false, directionLess: true }, - }; - - static nextPairedChar( - position: Position, - charToMatch: string, - closed: boolean = true - ): Position | undefined { - /** - * We do a fairly basic implementation that only tracks the state of the type of - * character you're over and its pair (e.g. "[" and "]"). This is similar to - * what Vim does. - * - * It can't handle strings very well - something like "|( ')' )" where | is the - * cursor will cause it to go to the ) in the quotes, even though it should skip over it. - * - * PRs welcomed! (TODO) - * Though ideally VSC implements https://github.com/Microsoft/vscode/issues/7177 - */ - const toFind = this.pairings[charToMatch]; - - if (toFind === undefined || toFind.directionLess) { - return undefined; - } - - let regex = new RegExp( - '(' + escapeRegExpCharacters(charToMatch) + '|' + escapeRegExpCharacters(toFind.match) + ')', - 'i' - ); - - let stackHeight = closed ? 0 : 1; - let matchedPosition: Position | undefined = undefined; - - // find matched bracket up - if (!toFind.nextMatchIsForward) { - for (let lineNumber = position.line; lineNumber >= 0; lineNumber--) { - let lineText = TextEditor.getLineAt(new Position(lineNumber, 0)).text; - let startOffset = - lineNumber === position.line ? lineText.length - position.character - 1 : 0; - - while (true) { - let queryText = toReversedString(lineText).substr(startOffset); - if (queryText === '') { - break; - } - - let m = queryText.match(regex); - - if (!m) { - break; - } - - let matchedChar = m[0]; - if (matchedChar === charToMatch) { - stackHeight++; - } - - if (matchedChar === toFind.match) { - stackHeight--; - } - - if (stackHeight === 0) { - matchedPosition = new Position( - lineNumber, - lineText.length - startOffset - m.index! - 1 - ); - return matchedPosition; - } - - startOffset = startOffset + m.index! + 1; - } - } - } else { - for ( - let lineNumber = position.line, lineCount = TextEditor.getLineCount(); - lineNumber < lineCount; - lineNumber++ - ) { - let lineText = TextEditor.getLineAt(new Position(lineNumber, 0)).text; - let startOffset = lineNumber === position.line ? position.character : 0; - - while (true) { - let queryText = lineText.substr(startOffset); - if (queryText === '') { - break; - } - - let m = queryText.match(regex); - - if (!m) { - break; - } - - let matchedChar = m[0]; - if (matchedChar === charToMatch) { - stackHeight++; - } - - if (matchedChar === toFind.match) { - stackHeight--; - } - - if (stackHeight === 0) { - matchedPosition = new Position(lineNumber, startOffset + m.index!); - return matchedPosition; - } - - startOffset = startOffset + m.index! + 1; - } - } - } - - if (matchedPosition) { - return matchedPosition; - } - - // TODO(bell) - return undefined; - } - - /** - * Given a current position, find an immediate following bracket and return the range. If - * no matching bracket is found immediately following the opening bracket, return undefined. - */ - static immediateMatchingBracket(currentPosition: Position): vscode.Range | undefined { - // Don't delete bracket unless autoClosingBrackets is set - if (!vscode.workspace.getConfiguration().get('editor.autoClosingBrackets')) { - return undefined; - } - - const deleteRange = new vscode.Range( - currentPosition, - currentPosition.getLeftThroughLineBreaks() - ); - const deleteText = vscode.window.activeTextEditor!.document.getText(deleteRange); - let matchRange: vscode.Range | undefined; - let isNextMatch = false; - - if ('{[("\'`'.indexOf(deleteText) > -1) { - const matchPosition = currentPosition.add(new PositionDiff(0, 1)); - matchRange = new vscode.Range(matchPosition, matchPosition.getLeftThroughLineBreaks()); - isNextMatch = - vscode.window.activeTextEditor!.document.getText(matchRange) === - PairMatcher.pairings[deleteText].match; - } - - if (isNextMatch && matchRange) { - return matchRange; - } - - return undefined; - } -} +import { Position, PositionDiff } from './../motion/position'; +import { TextEditor } from './../../textEditor'; +import * as vscode from 'vscode'; + +function escapeRegExpCharacters(value: string): string { + return value.replace(/[\-\\\{\}\*\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&'); +} + +let toReversedString = (function() { + function reverse(str: string): string { + let reversedStr = ''; + for (let i = str.length - 1; i >= 0; i--) { + reversedStr += str.charAt(i); + } + return reversedStr; + } + + let lastInput: string = ''; + let lastOutput: string = ''; + return function(str: string): string { + if (lastInput !== str) { + lastInput = str; + lastOutput = reverse(lastInput); + } + return lastOutput; + }; +})(); + +/** + * PairMatcher finds the position matching the given character, respecting nested + * instances of the pair. + */ +export class PairMatcher { + static pairings: { + [key: string]: { + match: string; + nextMatchIsForward: boolean; + directionLess?: boolean; + matchesWithPercentageMotion?: boolean; + }; + } = { + '(': { match: ')', nextMatchIsForward: true, matchesWithPercentageMotion: true }, + '{': { match: '}', nextMatchIsForward: true, matchesWithPercentageMotion: true }, + '[': { match: ']', nextMatchIsForward: true, matchesWithPercentageMotion: true }, + ')': { match: '(', nextMatchIsForward: false, matchesWithPercentageMotion: true }, + '}': { match: '{', nextMatchIsForward: false, matchesWithPercentageMotion: true }, + ']': { match: '[', nextMatchIsForward: false, matchesWithPercentageMotion: true }, + // These characters can't be used for "%"-based matching, but are still + // useful for text objects. + '<': { match: '>', nextMatchIsForward: true }, + '>': { match: '<', nextMatchIsForward: false }, + // These are useful for deleting closing and opening quotes, but don't seem to negatively + // affect how text objects such as `ci"` work, which was my worry. + '"': { match: '"', nextMatchIsForward: false, directionLess: true }, + "'": { match: "'", nextMatchIsForward: false, directionLess: true }, + '`': { match: '`', nextMatchIsForward: false, directionLess: true }, + }; + + static nextPairedChar( + position: Position, + charToMatch: string, + closed: boolean = true + ): Position | undefined { + /** + * We do a fairly basic implementation that only tracks the state of the type of + * character you're over and its pair (e.g. "[" and "]"). This is similar to + * what Vim does. + * + * It can't handle strings very well - something like "|( ')' )" where | is the + * cursor will cause it to go to the ) in the quotes, even though it should skip over it. + * + * PRs welcomed! (TODO) + * Though ideally VSC implements https://github.com/Microsoft/vscode/issues/7177 + */ + const toFind = this.pairings[charToMatch]; + + if (toFind === undefined || toFind.directionLess) { + return undefined; + } + + let regex = new RegExp( + '(' + escapeRegExpCharacters(charToMatch) + '|' + escapeRegExpCharacters(toFind.match) + ')', + 'i' + ); + + let stackHeight = closed ? 0 : 1; + let matchedPosition: Position | undefined = undefined; + + // find matched bracket up + if (!toFind.nextMatchIsForward) { + for (let lineNumber = position.line; lineNumber >= 0; lineNumber--) { + let lineText = TextEditor.getLineAt(new Position(lineNumber, 0)).text; + let startOffset = + lineNumber === position.line ? lineText.length - position.character - 1 : 0; + + while (true) { + let queryText = toReversedString(lineText).substr(startOffset); + if (queryText === '') { + break; + } + + let m = queryText.match(regex); + + if (!m) { + break; + } + + let matchedChar = m[0]; + if (matchedChar === charToMatch) { + stackHeight++; + } + + if (matchedChar === toFind.match) { + stackHeight--; + } + + if (stackHeight === 0) { + matchedPosition = new Position( + lineNumber, + lineText.length - startOffset - m.index! - 1 + ); + return matchedPosition; + } + + startOffset = startOffset + m.index! + 1; + } + } + } else { + for ( + let lineNumber = position.line, lineCount = TextEditor.getLineCount(); + lineNumber < lineCount; + lineNumber++ + ) { + let lineText = TextEditor.getLineAt(new Position(lineNumber, 0)).text; + let startOffset = lineNumber === position.line ? position.character : 0; + + while (true) { + let queryText = lineText.substr(startOffset); + if (queryText === '') { + break; + } + + let m = queryText.match(regex); + + if (!m) { + break; + } + + let matchedChar = m[0]; + if (matchedChar === charToMatch) { + stackHeight++; + } + + if (matchedChar === toFind.match) { + stackHeight--; + } + + if (stackHeight === 0) { + matchedPosition = new Position(lineNumber, startOffset + m.index!); + return matchedPosition; + } + + startOffset = startOffset + m.index! + 1; + } + } + } + + if (matchedPosition) { + return matchedPosition; + } + + // TODO(bell) + return undefined; + } + + /** + * Given a current position, find an immediate following bracket and return the range. If + * no matching bracket is found immediately following the opening bracket, return undefined. + */ + static immediateMatchingBracket(currentPosition: Position): vscode.Range | undefined { + // Don't delete bracket unless autoClosingBrackets is set + if (!vscode.workspace.getConfiguration().get('editor.autoClosingBrackets')) { + return undefined; + } + + const deleteRange = new vscode.Range( + currentPosition, + currentPosition.getLeftThroughLineBreaks() + ); + const deleteText = vscode.window.activeTextEditor!.document.getText(deleteRange); + let matchRange: vscode.Range | undefined; + let isNextMatch = false; + + if ('{[("\'`'.indexOf(deleteText) > -1) { + const matchPosition = currentPosition.add(new PositionDiff(0, 1)); + matchRange = new vscode.Range(matchPosition, matchPosition.getLeftThroughLineBreaks()); + isNextMatch = + vscode.window.activeTextEditor!.document.getText(matchRange) === + PairMatcher.pairings[deleteText].match; + } + + if (isNextMatch && matchRange) { + return matchRange; + } + + return undefined; + } +} diff --git a/tsconfig.json b/tsconfig.json index 9c54164725f..b0829f7d25d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,22 +1,22 @@ -{ - "compilerOptions": { - "allowUnusedLabels": false, - "module": "commonjs", - "target": "es6", - "outDir": "out", - "noImplicitAny": true, - "noImplicitReturns": true, - "suppressImplicitAnyIndexErrors": true, - "lib": [ - "es6" - ], - "sourceMap": true, - "strictNullChecks": true, - "experimentalDecorators": true, - "alwaysStrict": true - }, - "exclude": [ - "node_modules", - ".vscode-test" - ] -} +{ + "compilerOptions": { + "allowUnusedLabels": false, + "module": "commonjs", + "target": "es6", + "outDir": "out", + "noImplicitAny": true, + "noImplicitReturns": true, + "suppressImplicitAnyIndexErrors": true, + "lib": [ + "es6" + ], + "sourceMap": true, + "strictNullChecks": true, + "experimentalDecorators": true, + "alwaysStrict": true + }, + "exclude": [ + "node_modules", + ".vscode-test" + ] +}