diff --git a/package-lock.json b/package-lock.json index 71584db51..c7d8b1ba0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,1195 @@ { + "name": "csharp-language-server-protocol", + "lockfileVersion": 2, "requires": true, - "lockfileVersion": 1, + "packages": { + "": { + "devDependencies": { + "husky": "^4.3.0", + "lint-staged": "^10.5.1", + "prettier": "^2.1.2" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "node_modules/@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "dependencies": { + "type-fest": "^0.11.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "dependencies": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-versions": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", + "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", + "dev": true, + "dependencies": { + "semver-regex": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/husky": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.0.tgz", + "integrity": "sha512-tTMeLCLqSBqnflBZnlVDhpaIMucSGaYyX6855jM4AguGeWCeSzNdb1mfyWduTZ3pe3SJVvVWGL0jO1iKZVPfTA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "compare-versions": "^3.6.0", + "cosmiconfig": "^7.0.0", + "find-versions": "^3.2.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^4.2.0", + "please-upgrade-node": "^3.2.0", + "slash": "^3.0.0", + "which-pm-runs": "^1.0.0" + }, + "bin": { + "husky-run": "bin/run.js", + "husky-upgrade": "lib/upgrader/bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "node_modules/lint-staged": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.5.1.tgz", + "integrity": "sha512-fTkTGFtwFIJJzn/PbUO3RXyEBHIhbfYBE7+rJyLcOXabViaO/h6OslgeK6zpeUtzkDrzkgyAYDTLAwx6JzDTHw==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "commander": "^6.2.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.2.0", + "dedent": "^0.7.0", + "enquirer": "^2.3.6", + "execa": "^4.1.0", + "listr2": "^3.2.2", + "log-symbols": "^4.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "^3.3.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + } + }, + "node_modules/listr2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.2.2.tgz", + "integrity": "sha512-AajqcZEUikF2ioph6PfH3dIuxJclhr3i3kHgTOP0xeXdWQohrvJAAmqVcV43/GI987HFY/vzT73jYXoa4esDHg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "figures": "^3.2.0", + "indent-string": "^4.0.0", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rxjs": "^6.6.3", + "through": "^2.3.8" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true, + "bin": { + "opencollective-postinstall": "index.js" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.1.tgz", + "integrity": "sha512-ztoZ4/DYeXQq4E21v169sC8qWINGpcosGv9XhTDvg9/hWvx/zrFkc9BiWxR58OJLHGk28j5BL0SDLeV2WmFZlQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "dependencies": { + "semver-compare": "^1.0.0" + } + }, + "node_modules/prettier": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz", + "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "node_modules/semver-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", + "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "dev": true, + "engines": { + "node": ">= 6" + } + } + }, "dependencies": { "@babel/code-frame": { "version": "7.10.4", diff --git a/src/Protocol/Models/Position.Helpers.cs b/src/Protocol/Models/Position.Helpers.cs new file mode 100644 index 000000000..d02aaee4a --- /dev/null +++ b/src/Protocol/Models/Position.Helpers.cs @@ -0,0 +1,13 @@ +namespace OmniSharp.Extensions.LanguageServer.Protocol.Models +{ + public partial class Position + { + /// + /// Derive a new position from this position. + /// + public Position Delta(int deltaLine = 0, int deltaCharacter = 0) + { + return new Position(Line + deltaLine, Character + deltaCharacter); + } + } +} diff --git a/src/Protocol/Models/Position.cs b/src/Protocol/Models/Position.cs index 924d72def..53e73ca30 100644 --- a/src/Protocol/Models/Position.cs +++ b/src/Protocol/Models/Position.cs @@ -5,7 +5,7 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { [DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")] - public class Position : IEquatable, IComparable, IComparable + public partial class Position : IEquatable, IComparable, IComparable { public Position() { diff --git a/src/Protocol/Models/Range.Helpers.cs b/src/Protocol/Models/Range.Helpers.cs new file mode 100644 index 000000000..812517692 --- /dev/null +++ b/src/Protocol/Models/Range.Helpers.cs @@ -0,0 +1,409 @@ +using System; +using System.Collections.Generic; + +namespace OmniSharp.Extensions.LanguageServer.Protocol.Models +{ + + public partial class Range + { + /// + /// Test if this range is empty. + /// + public bool IsEmpty() => IsEmpty(this); + + /// + /// Test if `range` is empty. + /// + public static bool IsEmpty(Range range) => range.Start.Line == range.End.Line && range.Start.Character == range.End.Character; + + /// + /// Test if position is in this range. If the position is at the edges, will return true. + /// + public bool Contains(Position position) => ContainsPosition(this, position); + + /// + /// Test if `position` is in `range`. If the position is at the edges, will return true. + /// + public static bool ContainsPosition(Range range, Position position) + { + if (position.Line < range.Start.Line || position.Line > range.End.Line) + { + return false; + } + + if (position.Line == range.Start.Line && position.Character < range.Start.Character) + { + return false; + } + + if (position.Line == range.End.Line && position.Character > range.End.Character) + { + return false; + } + + return true; + } + + /// + /// Test if range is in this range. If the range is equal to this range, will return true. + /// + public bool Contains(Range range) => ContainsRange(this, range); + + /// + /// Test if `otherRange` is in `range`. If the ranges are equal, will return true. + /// + public static bool ContainsRange(Range range, Range otherRange) + { + if (otherRange.Start.Line < range.Start.Line || otherRange.End.Line < range.Start.Line) + { + return false; + } + + if (otherRange.Start.Line > range.End.Line || otherRange.End.Line > range.End.Line) + { + return false; + } + + if (otherRange.Start.Line == range.Start.Line && otherRange.Start.Character < range.Start.Character) + { + return false; + } + + if (otherRange.End.Line == range.End.Line && otherRange.End.Character > range.End.Character) + { + return false; + } + + return true; + } + + /// + /// Test if `range` is strictly in this range. `range` must start after and end before this range for the result to be true. + /// + public bool StrictContains(Range range) => StrictContainsRange(this, range); + + /// + /// Test if `otherRange` is strictly in `range` (must start after, and end before). If the ranges are equal, will return false. + /// + public static bool StrictContainsRange(Range range, Range otherRange) + { + if (otherRange.Start.Line < range.Start.Line || otherRange.End.Line < range.Start.Line) + { + return false; + } + + if (otherRange.Start.Line > range.End.Line || otherRange.End.Line > range.End.Line) + { + return false; + } + + if (otherRange.Start.Line == range.Start.Line && otherRange.Start.Character <= range.Start.Character) + { + return false; + } + + if (otherRange.End.Line == range.End.Line && otherRange.End.Character >= range.End.Character) + { + return false; + } + + return true; + } + + /// + /// A reunion of the two ranges. + /// The smallest position will be used as the start point, and the largest one as the end point. + /// + public static Range operator +(Range a, Range b) + { + return PlusRange(a, b); + } + + /// + /// A reunion of the two ranges. + /// The smallest position will be used as the start point, and the largest one as the end point. + /// + public static Range PlusRange(Range a, Range b) + { + int startLineNumber; + int startColumn; + int endLineNumber; + int endColumn; + + if (b.Start.Line < a.Start.Line) + { + startLineNumber = b.Start.Line; + startColumn = b.Start.Character; + } + else if (b.Start.Line == a.Start.Line) + { + startLineNumber = b.Start.Line; + startColumn = Math.Min(b.Start.Character, a.Start.Character); + } + else + { + startLineNumber = a.Start.Line; + startColumn = a.Start.Character; + } + + if (b.End.Line > a.End.Line) + { + endLineNumber = b.End.Line; + endColumn = b.End.Character; + } + else if (b.End.Line == a.End.Line) + { + endLineNumber = b.End.Line; + endColumn = Math.Max(b.End.Character, a.End.Character); + } + else + { + endLineNumber = a.End.Line; + endColumn = a.End.Character; + } + + return new Range(( startLineNumber, startColumn ), ( endLineNumber, endColumn )); + } + + /// + /// A intersection of the two ranges. + /// + public Range? Intersection(Range other) + { + return Intersection(this, other); + } + + /// + /// A intersection of the two ranges. + /// + public static Range? Intersection(Range a, Range b) + { + var resultStartLineNumber = a.Start.Line; + var resultStartColumn = a.Start.Character; + var resultEndLineNumber = a.End.Line; + var resultEndColumn = a.End.Character; + var otherStartLineNumber = b.Start.Line; + var otherStartColumn = b.Start.Character; + var otherEndLineNumber = b.End.Line; + var otherEndColumn = b.End.Character; + + if (resultStartLineNumber < otherStartLineNumber) + { + resultStartLineNumber = otherStartLineNumber; + resultStartColumn = otherStartColumn; + } + else if (resultStartLineNumber == otherStartLineNumber) + { + resultStartColumn = Math.Max(resultStartColumn, otherStartColumn); + } + + if (resultEndLineNumber > otherEndLineNumber) + { + resultEndLineNumber = otherEndLineNumber; + resultEndColumn = otherEndColumn; + } + else if (resultEndLineNumber == otherEndLineNumber) + { + resultEndColumn = Math.Min(resultEndColumn, otherEndColumn); + } + + // Check if selection is now empty + if (resultStartLineNumber > resultEndLineNumber) + { + return null; + } + + if (resultStartLineNumber == resultEndLineNumber && resultStartColumn > resultEndColumn) + { + return null; + } + + return new Range(( resultStartLineNumber, resultStartColumn ), ( resultEndLineNumber, resultEndColumn )); + } + + /// + /// Create a new empty range using this range's start position. + /// + public Range CollapseToStart() => CollapseToStart(this); + + /// + /// Create a new empty range using this range's start position. + /// + public static Range CollapseToStart(Range range) => new Range(range.Start, range.Start); + + /// + /// Create a new empty range using this range's start position. + /// + public Range CollapseToEnd() => CollapseToEnd(this); + + /// + /// Create a new empty range using this range's start position. + /// + public static Range CollapseToEnd(Range range) => new Range(range.End, range.End); + + public static bool IsBefore(Range a, Range b) => a.End.Line < b.Start.Line || a.End.Line == b.Start.Line && a.End.Character < b.Start.Character; + + public bool IsBefore(Range other) => IsBefore(this, other); + + public static bool IsBeforeOrTouching(Range a, Range b) => a.End.Line < b.Start.Line || a.End.Line == b.Start.Line && a.End.Character <= b.Start.Character; + + public bool IsBeforeOrTouching(Range other) => IsBeforeOrTouching(this, other); + + public static bool IsAfter(Range a, Range b) => b.End.Line < a.Start.Line || b.End.Line == a.Start.Line && b.End.Character < a.Start.Character; + + public bool IsAfter(Range other) => IsAfter(this, other); + + public static bool IsAfterOrTouching(Range a, Range b) => b.End.Line < a.Start.Line || b.End.Line == a.Start.Line && b.End.Character <= a.Start.Character; + + public bool IsAfterOrTouching(Range other) => IsAfterOrTouching(this, other); + + /// + /// Test if the two ranges are touching in any way. If the ranges are touching it returns false. + /// + public static bool AreIntersectingOrTouching(Range a, Range b) + { + // Check if `a` is before `b` + if (IsBefore(a, b)) + { + return false; + } + + // Check if `b` is before `a` + if (IsAfter(a, b)) + { + return false; + } + + // These ranges must intersect + return true; + } + + public bool IntersectsOrTouches(Range other) => AreIntersectingOrTouching(this, other); + + /// + /// Test if the two ranges are intersecting. If the ranges are touching it returns false. + /// + public bool Intersects(Range other) + { + return AreIntersecting(this, other); + } + + /// + /// Test if the two ranges are intersecting. If the ranges are touching it returns false. + /// + public static bool AreIntersecting(Range a, Range b) + { + // Check if `a` is before `b` + if (IsBeforeOrTouching(a, b)) + { + return false; + } + + // Check if `b` is before `a` + if (IsAfterOrTouching(a, b)) + { + return false; + } + + // These ranges must intersect + return true; + } + + public static IComparer AscendingComparer { get; } = new StartPositionComparer(); + public static IComparer CompareUsingStarts => AscendingComparer; + public static IComparer DescendingComparer { get; } = new EndPositionComparer(); + public static IComparer CompareUsingEnds => DescendingComparer; + + public class StartPositionComparer : IComparer + { + public int Compare(Range x, Range y) => CompareRangesUsingStarts(x, y); + } + + public class EndPositionComparer : IComparer + { + public int Compare(Range x, Range y) => CompareRangesUsingEnds(x, y); + } + + /// + /// A function that compares ranges, useful for sorting ranges + /// It will first compare ranges on the startPosition and then on the endPosition + /// + public static int CompareRangesUsingStarts(Range? a, Range? b) + { + if (a is not null && b is not null) + { + var aStartLineNumber = a.Start.Line | 0; + var bStartLineNumber = b.Start.Line | 0; + + if (aStartLineNumber == bStartLineNumber) + { + var aStartColumn = a.Start.Character | 0; + var bStartColumn = b.Start.Character | 0; + + if (aStartColumn == bStartColumn) + { + var aEndLineNumber = a.End.Line | 0; + var bEndLineNumber = b.End.Line | 0; + + if (aEndLineNumber == bEndLineNumber) + { + var aEndColumn = a.End.Character | 0; + var bEndColumn = b.End.Character | 0; + return aEndColumn - bEndColumn; + } + + return aEndLineNumber - bEndLineNumber; + } + + return aStartColumn - bStartColumn; + } + + return aStartLineNumber - bStartLineNumber; + } + + var aExists = a is not null ? 1 : 0; + var bExists = b is not null ? 1 : 0; + return aExists - bExists; + } + + /// + /// A function that compares ranges, useful for sorting ranges + /// It will first compare ranges on the endPosition and then on the startPosition + /// + public static int CompareRangesUsingEnds(Range a, Range b) + { + if (a.End.Line == b.End.Line) + { + if (a.End.Character == b.End.Character) + { + if (a.Start.Line == b.Start.Line) + { + return a.Start.Character - b.Start.Character; + } + + return a.Start.Line - b.Start.Line; + } + + return a.End.Character - b.End.Character; + } + + return a.End.Line - b.End.Line; + } + + /// + /// Test if the range spans multiple lines. + /// + public static bool SpansMultipleLines(Range range) + { + return range.End.Line > range.Start.Line; + } + + /// + /// Test if the range spans multiple lines. + /// + public bool SpansMultipleLines() + { + return SpansMultipleLines(this); + } + } +} diff --git a/src/Protocol/Models/Range.cs b/src/Protocol/Models/Range.cs index 4ee12bdfe..bfa376400 100644 --- a/src/Protocol/Models/Range.cs +++ b/src/Protocol/Models/Range.cs @@ -5,7 +5,7 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { [DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")] - public class Range : IEquatable + public partial class Range : IEquatable { public Range() { @@ -17,6 +17,12 @@ public Range(Position start, Position end) End = end; } + public Range(int startLine, int startCharacter, int endLine, int endCharacter) + { + Start = ( startLine, startCharacter ); + End = ( endLine, endCharacter ); + } + /// /// The range's start position. /// @@ -48,7 +54,7 @@ public override int GetHashCode() public static implicit operator Range((Position start, Position end) value) => new Range(value.start, value.end); - private string DebuggerDisplay => $"[start: {Start}, end: {End}]"; + private string DebuggerDisplay => $"[start: ({Start?.Line}, {Start?.Character}), end: ({End?.Line}, {End?.Character})]"; /// public override string ToString() => DebuggerDisplay; diff --git a/src/Testing/PositionMarker.cs b/src/Testing/PositionMarker.cs new file mode 100644 index 000000000..4f20e0ff7 --- /dev/null +++ b/src/Testing/PositionMarker.cs @@ -0,0 +1,73 @@ +using System; + +namespace OmniSharp.Extensions.LanguageProtocol.Testing +{ + public readonly struct PositionMarker : IEquatable, IComparable, IComparable + { + public char First { get; } + public char Second { get; } + public int CompareTo(PositionMarker other) + { + var firstComparison = First.CompareTo(other.First); + if (firstComparison != 0) return firstComparison; + return Second.CompareTo(other.Second); + } + + public int CompareTo(object? obj) + { + if (ReferenceEquals(null, obj)) return 1; + return obj is PositionMarker other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(PositionMarker)}"); + } + + public static bool operator <(PositionMarker left, PositionMarker right) => left.CompareTo(right) < 0; + + public static bool operator >(PositionMarker left, PositionMarker right) => left.CompareTo(right) > 0; + + public static bool operator <=(PositionMarker left, PositionMarker right) => left.CompareTo(right) <= 0; + + public static bool operator >=(PositionMarker left, PositionMarker right) => left.CompareTo(right) >= 0; + + public bool Equals(PositionMarker other) => First == other.First && Second == other.Second; + + public override bool Equals(object? obj) => obj is PositionMarker other && Equals(other); + + public override int GetHashCode() + { + unchecked + { + return ( First.GetHashCode() * 397 ) ^ Second.GetHashCode(); + } + } + + public static bool operator ==(PositionMarker left, PositionMarker right) => left.Equals(right); + + public static bool operator !=(PositionMarker left, PositionMarker right) => !left.Equals(right); + + + public PositionMarker(char first, char second) + { + First = first; + Second = second; + } + + public PositionMarker(string value) + { + if (value.Length != 2) throw new ArgumentOutOfRangeException(nameof(value), value, "Expected a string with 2 characters"); + First = value[0]; + Second = value[1]; + } + + public void Deconstruct(out char first, out char second) + { + first = First; + second = Second; + } + + public static implicit operator PositionMarker((char first, char second) value) + { + return new PositionMarker(value.first, value.second); + } + + public override string ToString() => $"{First}{Second}"; + } +} \ No newline at end of file diff --git a/src/Testing/TestConfigurationProvider.cs b/src/Testing/TestConfigurationProvider.cs index 68956c441..13da4030a 100644 --- a/src/Testing/TestConfigurationProvider.cs +++ b/src/Testing/TestConfigurationProvider.cs @@ -160,7 +160,7 @@ private T SetValueToToken(JToken root, string key, T value) return (T) arr2[i]; } - return (root[key] as T)!; + return ( root[key] as T )!; } private static JToken? GetValueFromToken(JToken root, string key) diff --git a/src/Testing/TestContent.cs b/src/Testing/TestContent.cs new file mode 100644 index 000000000..a9c52d607 --- /dev/null +++ b/src/Testing/TestContent.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; + +namespace OmniSharp.Extensions.LanguageProtocol.Testing +{ + /// + /// MarkupCode allows encoding additional pieces of information along with a piece of source code + /// that are useful for testing. The following information can be encoded: + /// + /// $$ - The index in the code. There can be no more than one of these. + /// + /// [| ... |] - A span in the code. There can be many of these and they can be nested. + /// + /// {|Name| ... |} - A span of code that is annotated with a name. There can be many of these and + /// they can be nested. + /// + /// This is similar the MarkupTestFile used in Roslyn: + /// https://github.com/dotnet/roslyn/blob/master/src/Test/Utilities/Shared/MarkedSource/MarkupTestFile.cs + /// + /// + /// Taken from OmniSharp source and modified + /// + public class TestContent + { + private readonly int? _index; + private readonly ImmutableDictionary> _spans; + + private TestContent(string code, int? index, ImmutableDictionary> spans) + { + Code = code; + Lines = ParseLines(code).ToImmutableArray(); + _index = index; + _spans = spans; + } + + public string Code { get; } + public ImmutableArray Lines { get; } + public int Index => _index ?? -1; + public bool HasIndex => _index.HasValue; + + public ImmutableList GetRanges(string? name = null) + { + if (_spans.TryGetValue(name ?? string.Empty, out var result)) + { + return result; + } + + return ImmutableList.Empty; + } + + public Position GetPositionAtIndex(int? index = null) => TestSourceHelpers.GetPositionAtIndex(Code, index ?? Index); + public int GetIndexAtPosition(Position position) => TestSourceHelpers.GetIndexAtPosition(Lines, position); + + private static IEnumerable ParseLines(string source) + { + var lastStart = 0; + var length = source.Length; + for (var index = 0; index < length; index++) + { + if (source[index] == '\n') + { + yield return source.Substring(lastStart, Math.Min(index + 1, length) - lastStart); + lastStart = index + 1; + } + } + + yield return source.Substring(lastStart); + } + + public static TestContent Parse(string input, TestContentOptions? options = null) + { + options ??= new TestContentOptions(); + // TODO: Should this be configurable? + input = input.NormalizeLineEndings(); + var markupLength = input.Length; + var codeBuilder = new StringBuilder(markupLength); + + int? position = null; + var spanStartStack = new Stack(); + var namedSpanStartStack = new Stack<(int spanStart, string spanName)>(); + var spans = new Dictionary>(); + + var codeIndex = 0; + var markupIndex = 0; + + var positionMarker = options.PositionMarker; + var rangeMarker = options.RangeMarker; + var namedMarker = options.NamedRangeMarker; + + while (markupIndex < markupLength) + { + var ch = input[markupIndex]; + + if (ch == positionMarker.First) + { + if (position == null && + markupIndex + 1 < markupLength && + input[markupIndex + 1] == positionMarker.Second) + { + position = codeIndex; + markupIndex += 2; + continue; + } + } + else if (ch == rangeMarker.open.First) + { + if (markupIndex + 1 < markupLength && + input[markupIndex + 1] == rangeMarker.open.Second) + { + spanStartStack.Push(codeIndex); + markupIndex += 2; + continue; + } + } + else if (ch == namedMarker.open.First) + { + if (markupIndex + 1 < markupLength && + input[markupIndex + 1] == namedMarker.open.Second) + { + var nameIndex = markupIndex + 2; + var nameStartIndex = nameIndex; + var nameLength = 0; + var found = false; + + // Parse out name + while (nameIndex < markupLength) + { + if (input[nameIndex] == namedMarker.labelStop) + { + found = true; + break; + } + + nameLength++; + nameIndex++; + } + + if (found) + { + var name = input.Substring(nameStartIndex, nameLength); + namedSpanStartStack.Push(( codeIndex, name )); + markupIndex = nameIndex + 1; // Move after ':' + continue; + } + + // We didn't find a ':'. In this case, we just carry on... + } + } + else if (ch == rangeMarker.close.First || ch == namedMarker.close.First) + { + if (markupIndex + 1 < markupLength) + { + if (ch == rangeMarker.close.First && input[markupIndex + 1] == rangeMarker.close.Second) + { + if (spanStartStack.Count == 0) + { + throw new ArgumentException($"Saw {rangeMarker.close} without matching {rangeMarker.open}"); + } + + var spanStart = spanStartStack.Pop(); + + AddSpan(spans, string.Empty, spanStart, codeIndex); + markupIndex += 2; + + continue; + } + + if (ch == namedMarker.close.First && input[markupIndex + 1] == namedMarker.close.Second) + { + if (namedSpanStartStack.Count == 0) + { + throw new ArgumentException($"Saw {namedMarker.close} without matching {namedMarker.open}"); + } + + var tuple = namedSpanStartStack.Pop(); + var spanStart = tuple.Item1; + var spanName = tuple.Item2; + + AddSpan(spans, spanName, spanStart, codeIndex); + markupIndex += 2; + + continue; + } + } + } + + codeBuilder.Append(ch); + codeIndex++; + markupIndex++; + } + + var source = codeBuilder.ToString(); + var finalSpans = spans + .ToImmutableDictionary( + keySelector: kvp => kvp.Key, + elementSelector: kvp => kvp.Value + .Select(z => new Range(TestSourceHelpers.GetPositionAtIndex(source, z.start), TestSourceHelpers.GetPositionAtIndex(source, z.end))) + .ToImmutableList() + .Sort(Range.AscendingComparer) + ); + + return new TestContent(source, position, finalSpans); + } + + private static void AddSpan(Dictionary> spans, string spanName, int spanStart, int spanEnd) + { + if (!spans.TryGetValue(spanName, out var spanList)) + { + spanList = new List<(int start, int end)>(); + spans.Add(spanName, spanList); + } + + spanList.Add(( spanStart, spanEnd )); + } + } +} diff --git a/src/Testing/TestContentOptions.cs b/src/Testing/TestContentOptions.cs new file mode 100644 index 000000000..e0cbe11a8 --- /dev/null +++ b/src/Testing/TestContentOptions.cs @@ -0,0 +1,9 @@ +namespace OmniSharp.Extensions.LanguageProtocol.Testing +{ + public class TestContentOptions + { + public PositionMarker PositionMarker { get; set; } = ( '$', '$' ); + public (PositionMarker open, char labelStop, PositionMarker close) NamedRangeMarker { get; set; } = ( ('{', '|'), ':', ('|', '}') ); + public (PositionMarker open, PositionMarker close) RangeMarker { get; set; } = ( ('[', '|'), ('|', ']') ); + } +} \ No newline at end of file diff --git a/src/Testing/TestSourceHelpers.cs b/src/Testing/TestSourceHelpers.cs new file mode 100644 index 000000000..5b3e588ec --- /dev/null +++ b/src/Testing/TestSourceHelpers.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; + +namespace OmniSharp.Extensions.LanguageProtocol.Testing +{ + public static class TestSourceHelpers + { + public static Position GetPositionAtIndex(string source, int index) + { + var line = 0; + var span = source.AsSpan(); + var rollingIndex = 0; + do + { + var location = span.IndexOf('\n'); + if (location == -1) + { + if (rollingIndex + span.Length >= index) + { + return new Position(line, index - rollingIndex); + } + + return ( line, span.Length ); + } + + if (rollingIndex + location >= index) + { + return new Position(line, index - rollingIndex); + } + + span = span.Slice(location + 1); + rollingIndex += location + 1; + line++; + if (rollingIndex == index) + { + return new Position(line, 0); + } + } while (!span.IsEmpty); + + return ( line, 0 ); + } + + public static string NormalizeLineEndings(this string value) => value.Replace("\r\n", "\n"); + + public static string ExtractRange(this TestContent source, Range range) + { + var start = source.GetIndexAtPosition(range.Start); + return source.Code.Substring(start, source.GetIndexAtPosition(range.End) - start); + } + + public static IEnumerable ExtractRanges(this TestContent source, IEnumerable ranges) + { + foreach (var range in ranges) + { + var start = source.GetIndexAtPosition(range.Start); + yield return source.Code.Substring(start, source.GetIndexAtPosition(range.End) - start); + } + } + + public static int GetIndexAtPosition(IReadOnlyList lines, Position position) + { + if (position.Line >= lines.Count) return -1; + var characterCount = lines + .Take(position.Line) + .Aggregate(0, (acc, v) => acc + v.Length); + return characterCount + position.Character; + } + + public static int GetIndexAtPosition(in string[] lines, Position position) + { + if (position.Line >= lines.Length) return -1; + var characterCount = lines + .Take(position.Line) + .Aggregate(0, (acc, v) => acc + v.Length); + return characterCount + position.Character; + } + } +} diff --git a/test/Lsp.Tests/Models/PositionTests.cs b/test/Lsp.Tests/Models/PositionTests.cs index a869f3b4a..cb743abb3 100644 --- a/test/Lsp.Tests/Models/PositionTests.cs +++ b/test/Lsp.Tests/Models/PositionTests.cs @@ -52,5 +52,17 @@ public void Is_Sortable_By_Line() a.Should().BeLessOrEqualTo(c); a.Should().BeGreaterOrEqualTo(c); } + + [Fact] + public void Should_Support_Delta() + { + var a = new Position(1, 1); + a = a.Delta(deltaLine: 1); + a.Line.Should().Be(2); + a = a.Delta(deltaCharacter: -1); + a.Character.Should().Be(0); + a = a.Delta(-1, 1); + a.Should().Be(( 1, 1 )); + } } } diff --git a/test/Lsp.Tests/Models/RangeTests.cs b/test/Lsp.Tests/Models/RangeTests.cs index 17e63b385..fcbec3734 100644 --- a/test/Lsp.Tests/Models/RangeTests.cs +++ b/test/Lsp.Tests/Models/RangeTests.cs @@ -1,3 +1,4 @@ +using System.Linq; using FluentAssertions; using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -20,5 +21,259 @@ public void SimpleTest(string expected) var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject(expected); deresult.Should().BeEquivalentTo(model); } + + [Fact] + public void Range_Is_Empty() + { + var s = new Range(1, 1, 1, 1); + s.IsEmpty().Should().BeTrue(); + } + + [Fact] + public void Range_Equality() + { + var a = new Range(1, 1, 1, 1); + var b = new Range(1, 1, 1, 1); + var c = new Range(1, 1, 1, 2); + a.Should().Be(b); + a.Should().NotBe(c); + } + + [Theory] + [InlineData(1, 2, 1, 1)] + [InlineData(2, 1, 1, 2)] + [InlineData(1, 1, 1, 2)] + [InlineData(1, 1, 2, 1)] + public void Range_Is_Not_Empty(int startLine, int startCharacter, int endLine, int endCharacter) + { + var s = new Range(startLine, startCharacter, endLine, endCharacter); + s.IsEmpty().Should().BeFalse(); + } + + [Theory] + [InlineData(1, 1, 1, 3, 1, 2, 1, 4, "lt", "a.start < b.start, a.end < b.end")] + [InlineData(1, 1, 1, 3, 1, 1, 1, 4, "lt", "a.start = b.start, a.end < b.end")] + [InlineData(1, 2, 1, 3, 1, 1, 1, 4, "lt", "a.start > b.start, a.end < b.end")] + [InlineData(1, 1, 1, 4, 1, 2, 1, 4, "lt", "a.start < b.start, a.end = b.end")] + [InlineData(1, 1, 1, 4, 1, 1, 1, 4, "eq", "a.start = b.start, a.end = b.end")] + [InlineData(1, 2, 1, 4, 1, 1, 1, 4, "gt", "a.start > b.start, a.end = b.end")] + [InlineData(1, 1, 1, 5, 1, 2, 1, 4, "gt", "a.start < b.start, a.end > b.end")] + [InlineData(1, 1, 2, 4, 1, 1, 1, 4, "gt", "a.start = b.start, a.end > b.end")] + [InlineData(1, 2, 5, 1, 1, 1, 1, 4, "gt", "a.start > b.start, a.end > b.end")] + public void Compare_Ranges_Using_Ends(int startLineA, int startCharacterA, int endLineA, int endCharacterA, int startLineB, int startCharacterB, int endLineB, int endCharacterB, string @operator, string because) + { + var a = new Range(startLineA, startCharacterA, endLineA, endCharacterA); + var b = new Range(startLineB, startCharacterB, endLineB, endCharacterB); + + ( @operator switch { + "lt" => Range.CompareRangesUsingEnds(a, b) < 0, + "eq" => Range.CompareRangesUsingEnds(a, b) == 0, + "gt" => Range.CompareRangesUsingEnds(a, b) > 0, + _ => false + } ).Should().BeTrue(because); + + } + + [Theory] + [InlineData(1, 1, 1, 3, 1, 2, 1, 4, "lt", "a.start < b.start, a.end < b.end")] + [InlineData(1, 1, 1, 5, 1, 2, 1, 4, "lt", "a.start < b.start, a.end > b.end")] + [InlineData(1, 1, 1, 4, 1, 2, 1, 4, "lt", "a.start < b.start, a.end = b.end")] + [InlineData(1, 1, 1, 3, 1, 1, 1, 4, "lt", "a.start = b.start, a.end < b.end")] + [InlineData(1, 1, 1, 4, 1, 1, 1, 4, "eq", "a.start = b.start, a.end = b.end")] + [InlineData(1, 1, 2, 4, 1, 1, 1, 4, "gt", "a.start = b.start, a.end > b.end")] + [InlineData(1, 2, 1, 4, 1, 1, 1, 4, "gt", "a.start > b.start, a.end = b.end")] + [InlineData(1, 2, 1, 3, 1, 1, 1, 4, "gt", "a.start > b.start, a.end < b.end")] + [InlineData(1, 2, 5, 1, 1, 1, 1, 4, "gt", "a.start > b.start, a.end > b.end")] + public void Compare_Ranges_Using_Starts(int startLineA, int startCharacterA, int endLineA, int endCharacterA, int startLineB, int startCharacterB, int endLineB, int endCharacterB, string @operator, string because) + { + var a = new Range(startLineA, startCharacterA, endLineA, endCharacterA); + var b = new Range(startLineB, startCharacterB, endLineB, endCharacterB); + + ( @operator switch { + "lt" => Range.CompareRangesUsingStarts(a, b) < 0, + "eq" => Range.CompareRangesUsingStarts(a, b) == 0, + "gt" => Range.CompareRangesUsingStarts(a, b) > 0, + _ => false + } ).Should().BeTrue(because); + + } + + [Theory] + [InlineData(2, 2, 5, 10, 1, 3, false)] + [InlineData(2, 2, 5, 10, 2, 1, false)] + [InlineData(2, 2, 5, 10, 2, 2, true)] + [InlineData(2, 2, 5, 10, 2, 3, true)] + [InlineData(2, 2, 5, 10, 3, 1, true)] + [InlineData(2, 2, 5, 10, 5, 9, true)] + [InlineData(2, 2, 5, 10, 5, 10, true)] + [InlineData(2, 2, 5, 10, 5, 11, false)] + [InlineData(2, 2, 5, 10, 6, 1, false)] + public void Range_Contains_Position(int startLine, int startCharacter, int endLine, int endCharacter, int positionLine, int positionCharacter, bool result) + { + var range = new Range(startLine, startCharacter, endLine, endCharacter); + range.Contains(new Position(positionLine, positionCharacter)).Should().Be(result); + } + + [Theory] + [InlineData(2, 2, 5, 10, 1, 3, 2, 2, false)] + [InlineData(2, 2, 5, 10, 2, 1, 2, 2, false)] + [InlineData(2, 2, 5, 10, 2, 2, 5, 11, false)] + [InlineData(2, 2, 5, 10, 2, 2, 6, 1, false)] + [InlineData(2, 2, 5, 10, 5, 9, 6, 1, false)] + [InlineData(2, 2, 5, 10, 5, 10, 6, 1, false)] + [InlineData(2, 2, 5, 10, 2, 2, 5, 10, true)] + [InlineData(2, 2, 5, 10, 2, 3, 5, 9, true)] + [InlineData(2, 2, 5, 10, 3, 100, 4, 100, true)] + public void Range_Contains_Range(int startLine, int startCharacter, int endLine, int endCharacter, int startLineB, int startCharacterB, int endLineB, int endCharacterB, bool result) + { + var rangeA = new Range(startLine, startCharacter, endLine, endCharacter); + var rangeB = new Range(startLineB, startCharacterB, endLineB, endCharacterB); + rangeA.Contains(rangeB).Should().Be(result); + } + + [Theory] + [InlineData(2, 2, 5, 10, 1, 3, 2, 2, false)] + [InlineData(2, 2, 5, 10, 2, 1, 2, 2, false)] + [InlineData(2, 2, 5, 10, 2, 2, 5, 11, false)] + [InlineData(2, 2, 5, 10, 2, 2, 6, 1, false)] + [InlineData(2, 2, 5, 10, 5, 9, 6, 1, false)] + [InlineData(2, 2, 5, 10, 5, 10, 6, 1, false)] + [InlineData(2, 2, 5, 9, 2, 2, 5, 10, false)] + [InlineData(2, 2, 5, 10, 2, 2, 5, 10, false)] + [InlineData(2, 1, 5, 10, 2, 2, 5, 10, false)] + [InlineData(2, 2, 5, 10, 2, 3, 5, 9, true)] + [InlineData(2, 2, 5, 10, 3, 100, 4, 100, true)] + public void Range_Strictly_Contains_Range(int startLine, int startCharacter, int endLine, int endCharacter, int startLineB, int startCharacterB, int endLineB, int endCharacterB, bool result) + { + var rangeA = new Range(startLine, startCharacter, endLine, endCharacter); + var rangeB = new Range(startLineB, startCharacterB, endLineB, endCharacterB); + rangeA.StrictContains(rangeB).Should().Be(result); + } + + [Theory] + [InlineData(2, 2, 3, 2, 4, 2, 5, 2, false)] + [InlineData(4, 2, 5, 2, 2, 2, 3, 2, false)] + [InlineData(4, 2, 5, 2, 5, 2, 6, 2, false)] + [InlineData(5, 2, 6, 2, 4, 2, 5, 2, false)] + [InlineData(2, 2, 2, 7, 2, 4, 2, 6, true)] + [InlineData(2, 2, 2, 7, 2, 4, 2, 9, true)] + [InlineData(2, 4, 2, 9, 2, 2, 2, 7, true)] + public void Range_Are_Intersecting(int startLine, int startCharacter, int endLine, int endCharacter, int startLineB, int startCharacterB, int endLineB, int endCharacterB, bool result) + { + var rangeA = new Range(startLine, startCharacter, endLine, endCharacter); + var rangeB = new Range(startLineB, startCharacterB, endLineB, endCharacterB); + Range.AreIntersecting(rangeA, rangeB).Should().Be(result); + } + + [Fact] + public void Ranges_Can_Be_Added() + { + var a = new Range(1, 1, 2, 2); + var b = new Range(3,3, 4,0); + + ( a + b ).Should().Be(new Range(1, 1, 4, 0)); + } + + [Fact] + public void Ranges_Can_Span_Lines() + { + var a = new Range(1, 1, 1, 2); + var b = new Range(3,3, 4,0); + + a.SpansMultipleLines().Should().BeFalse(); + b.SpansMultipleLines().Should().BeTrue(); + } + + [Fact] + public void Ranges_Can_Collapse() + { + var a = new Range(1, 1, 2, 2); + + a.CollapseToStart().Should().Be(new Range(a.Start, a.Start)); + a.CollapseToEnd().Should().Be(new Range(a.End, a.End)); + } + + [Fact] + public void Ranges_Can_Be_Intersected() + { + var a = new Range(1, 1, 1, 3); + var b = new Range(2, 1, 2, 2); + var c = new Range(1,2, 4, 0); + + a.Intersection(b).Should().BeNull(); + b.Intersection(a).Should().BeNull(); + a.Intersection(a).Should().Be(a); + + c.Intersection(b).Should().Be(b); + b.Intersection(c).Should().Be(b); + c.Intersection(a).Should().Be(new Range(1,2, 1, 3)); + a.Intersection(c).Should().Be(new Range(1,2, 1, 3)); + } + + [Fact] + public void Ranges_Compare_Compare_With_Or_Without_Touching() + { + var a = new Range(1, 1, 1, 3); + var b = new Range(2, 1, 2, 3); + var c = new Range(1, 3, 2, 1); + + a.IsBefore(b).Should().BeTrue(); + b.IsAfter(a).Should().BeTrue(); + a.IsBefore(c).Should().BeFalse(); + b.IsAfter(c).Should().BeFalse(); + + a.IsBeforeOrTouching(c).Should().BeTrue(); + b.IsAfterOrTouching(c).Should().BeTrue(); + } + + [Fact] + public void Ranges_Check_For_Intersections() + { + var a = new Range(1, 1, 1, 3); + var b = new Range(2, 1, 2, 3); + var c = new Range(1, 3, 2, 1); + + a.Intersects(b).Should().BeFalse(); + a.IntersectsOrTouches(b).Should().BeFalse(); + + a.Intersects(c).Should().BeFalse(); + a.IntersectsOrTouches(c).Should().BeTrue(); + + b.Intersects(c).Should().BeFalse(); + b.IntersectsOrTouches(c).Should().BeTrue(); + + } + + [Fact] + public void Ranges_Can_Be_Sorted_By_Start() + { + var a = new Range(0, 0, 0, 1); + var b = new Range(0, 0, 0, 5); + var c = new Range(1, 0, 1, 1); + var d = new Range(2, 0, 2, 1); + var e = new Range(3, 0, 3, 1); + + var list = new[] { b, a, d, e, c }; + + list.OrderBy(z => z, Range.CompareUsingStarts) + .Should() + .ContainInOrder(a, b, c, d); + } + + [Fact] + public void Ranges_Can_Be_Sorted_By_End() + { + var a = new Range(0, 0, 0, 1); + var b = new Range(0, 0, 0, 5); + var c = new Range(1, 0, 1, 1); + var d = new Range(2, 0, 2, 1); + var e = new Range(3, 0, 3, 1); + + var list = new[] { b, a, d, e, c }; + + list.OrderByDescending(z => z, Range.CompareUsingEnds) + .Should() + .ContainInOrder(e, d, c, b, a); + } } } diff --git a/test/Lsp.Tests/Testing/TestContentTests.cs b/test/Lsp.Tests/Testing/TestContentTests.cs new file mode 100644 index 000000000..7c0a9e53e --- /dev/null +++ b/test/Lsp.Tests/Testing/TestContentTests.cs @@ -0,0 +1,229 @@ +using System; +using FluentAssertions; +using NSubstitute; +using OmniSharp.Extensions.LanguageProtocol.Testing; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using Xunit; +using Xunit.Abstractions; + +namespace Lsp.Tests.Testing +{ + public class TestContentTests : AutoTestBase + { + public TestContentTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + + [Fact] + public void Should_Parse_Locations_1() + { + var content = TestContent.Parse(@"012$$3456789"); + content.Code.Should().Be("0123456789"); + content.Index.Should().Be(3); + content.Lines.Should().HaveCount(1); + + content.GetPositionAtIndex().Should().Be(new Position(0, 3)); + content.GetIndexAtPosition(new Position(0, 5)).Should().Be(5); + } + + [Theory] + [InlineData(@"01|]2345", "Saw |] without matching [|")] + [InlineData(@"01|}name:2345", "Saw |} without matching {|")] + public void Throw_Error_On_Range(string text, string message) + { + Action a = () => TestContent.Parse(text); + a.Should().Throw().WithMessage(message); + } + + [Fact] + public void Should_Parse_Locations_2() + { + var content = TestContent.Parse( + @"0 +1 +2 +$$3 +4 +5 +6 +7 +8 +9" + ); + content.Code.Should().Be( + @"0 +1 +2 +3 +4 +5 +6 +7 +8 +9".NormalizeLineEndings() + ); + content.Index.Should().Be(6); + content.Lines.Should().HaveCount(10); + + content.GetPositionAtIndex().Should().Be(new Position(3, 0)); + content.GetIndexAtPosition(new Position(4, 0)).Should().Be(8); + } + + [Theory] + [InlineData('{', '}')] + [InlineData('[', ']')] + [InlineData('(', ')')] + public void Position_Marker_Should_be_configurable(char first, char end) + { + var content = $"hello {first}{end}this is a test"; + var testContent = TestContent.Parse(content, new TestContentOptions() { + PositionMarker = (first, end) + }); + testContent.HasIndex.Should().BeTrue(); + testContent.Index.Should().Be(6); + } + + [Theory] + [InlineData('{', '}', '|')] + [InlineData('[', ']', '|')] + [InlineData('(', ')', '|')] + public void Ranges_Should_be_configurable(char start, char end, char term) + { + var content = $"hello {start}{term}this is a {term}{end}test"; + var testContent = TestContent.Parse(content, new TestContentOptions() { + RangeMarker = ((start, term), (term, end)) + }); + testContent.GetRanges().Should().HaveCount(1); + testContent.ExtractRange(testContent.GetRanges()[0]).Should().Be("this is a "); + } + + [Theory] + [InlineData('{', '}', '|', '?')] + [InlineData('-', '-', '|', ':')] + [InlineData('(', ')', '|', '-')] + public void Named_Ranges_Should_be_configurable(char start, char end, char term, char nameEnd) + { + var content = $"hello {start}{term}test{nameEnd}this is a {term}{end}test"; + var testContent = TestContent.Parse(content, new TestContentOptions() { + NamedRangeMarker = ((start, term), nameEnd, (term, end)) + }); + testContent.GetRanges("test").Should().HaveCount(1); + testContent.ExtractRange(testContent.GetRanges("test")[0]).Should().Be("this is a "); + } + + [Fact] + public void Should_Parse_Spans_1() + { + var content = TestContent.Parse( + @" +[|if (true) { + [|var a = 1;|] + [|var b = 2;|] + [|var c = 3;|] +[|var other = new { + [|value = true|] +};|] +|]" + ); + content.Code.Should().Be( + @" +if (true) { + var a = 1; + var b = 2; + var c = 3; +var other = new { + value = true +}; +".NormalizeLineEndings() + ); + content.Index.Should().Be(-1); + content.Lines.Should().HaveCount(9); + + + var ranges = content.GetRanges(); + ranges.Should().HaveCount(6); + content.ExtractRange(ranges[0]).Should().Be( + @"if (true) { + var a = 1; + var b = 2; + var c = 3; +var other = new { + value = true +}; +".NormalizeLineEndings() + ); + content.ExtractRange(ranges[1]).Should().Be(@"var a = 1;"); + content.ExtractRange(ranges[2]).Should().Be(@"var b = 2;"); + content.ExtractRange(ranges[3]).Should().Be(@"var c = 3;"); + content.ExtractRange(ranges[4]).Should().Be( + @"var other = new { + value = true +};".NormalizeLineEndings() + ); + content.ExtractRange(ranges[5]).Should().Be(@"value = true"); + } + + [Fact] + public void Should_Parse_Named_Spans_1() + { + var content = TestContent.Parse( + @" +{|first:if (true) { + {|a:var a = 1;|} + {|b:var b = 2;|} + {|c:var c = 3;|} +{|other:var other = new { + {|other:value = true|} +};|} +|}" + ); + content.Code.Should().Be( + @" +if (true) { + var a = 1; + var b = 2; + var c = 3; +var other = new { + value = true +}; +".NormalizeLineEndings() + ); + content.Index.Should().Be(-1); + content.Lines.Should().HaveCount(9); + + + var ranges = content.GetRanges("first"); + ranges.Should().HaveCount(1); + content.ExtractRange(ranges[0]).Should().Be( + @"if (true) { + var a = 1; + var b = 2; + var c = 3; +var other = new { + value = true +}; +".NormalizeLineEndings() + ); + ranges = content.GetRanges("a"); + ranges.Should().HaveCount(1); + content.ExtractRange(ranges[0]).Should().Be(@"var a = 1;"); + + ranges = content.GetRanges("b"); + ranges.Should().HaveCount(1); + content.ExtractRange(ranges[0]).Should().Be(@"var b = 2;"); + + ranges = content.GetRanges("c"); + ranges.Should().HaveCount(1); + content.ExtractRange(ranges[0]).Should().Be(@"var c = 3;"); + + ranges = content.GetRanges("other"); + ranges.Should().HaveCount(2); + content.ExtractRange(ranges[0]).Should().Be( + @"var other = new { + value = true +};".NormalizeLineEndings() + ); + content.ExtractRange(ranges[1]).Should().Be(@"value = true"); + } + } +}