diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 4c28f83..217dc70 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -3,7 +3,7 @@ name: Bug report about: Create a report to help us improve title: '' labels: bug -assignees: maxbertinetti +assignees: nobodywasishere --- diff --git a/.gitignore b/.gitignore index f341f2e..09cdaf5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ -out -node_modules -.directory -.vscode-test -crystal-lang-*.vsix \ No newline at end of file +/out +/node_modules +/.directory +/.vscode-test + +crystal-lang-*.vsix diff --git a/.vscode/launch.json b/.vscode/launch.json index 2da0392..7e52bdb 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,10 +7,13 @@ "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", - "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], - "stopOnEntry": false, + "args": [ + "--extensionDevelopmentPath=${workspaceRoot}" + ], "sourceMaps": true, - "outFiles": [ "${workspaceRoot}/out/src/**/*.js" ], + "outFiles": [ + "${workspaceRoot}/out/main.js" + ], "preLaunchTask": "tsc: build" }, { @@ -18,10 +21,14 @@ "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", - "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ], - "stopOnEntry": false, + "args": [ + "--extensionDevelopmentPath=${workspaceRoot}", + "--extensionTestsPath=${workspaceRoot}/out/test" + ], "sourceMaps": true, - "outFiles": [ "${workspaceRoot}/out/test/**/*.js" ], + "outFiles": [ + "${workspaceRoot}/out/main.js" + ], "preLaunchTask": "tsc: build" } ] diff --git a/.vscode/settings.json b/.vscode/settings.json index d137133..22de090 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,4 +6,4 @@ "search.exclude": { "out": true // set this to false to include "out" folder in search results } -} \ No newline at end of file +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9133c3b..862859c 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -10,11 +10,11 @@ "command": "npm", "args": [ "run", - "compile" + "esbuild" ], "problemMatcher": [ "$tsc-watch" ] } ] -} \ No newline at end of file +} diff --git a/.vscodeignore b/.vscodeignore index 576fd4c..d6c4536 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,9 +1,7 @@ -.vscode/** -.vscode-test/** -out/test/** -test/** -src/** -**/*.map +.vscode/ +test/ +src/ +out/main.js.map .gitignore tsconfig.json -.directory \ No newline at end of file +node_modules/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 61d534e..8a866b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,30 @@ # Change Log -## [Unreleased] -The Changelog will be updated on the upcoming v1.0 release +## [0.9.0] - 2024-02-01 -### Todo -- See [roadmap](https://github.com/crystal-lang-tools/vscode-crystal-lang/wiki/Roadmap). +### Fix + +- Color change when adding return type to class method [#157](https://github.com/crystal-lang-tools/vscode-crystal-lang/issues/157) +- Error executing Crystal plugin. spawn crystal ENOENT [#102](https://github.com/crystal-lang-tools/vscode-crystal-lang/issues/102), + Error executing Crystal plugin. spawn bash ENOENT in Windows [#172](https://github.com/crystal-lang-tools/vscode-crystal-lang/issues/172) +- Auto-formatting issue with some keywords [#158](https://github.com/crystal-lang-tools/vscode-crystal-lang/issues/102) +- Hover feature does not work with Windows11 PC and v0.8.4 of the extension (Native Windows Crystal, not WSL) [#176](https://github.com/crystal-lang-tools/vscode-crystal-lang/issues/176) +- With compiler path set properly still get error upon opening first .cr file [#177](https://github.com/crystal-lang-tools/vscode-crystal-lang/issues/177) +- Max processes not respected (there's now only one instance of the compiler running at once) [#122](https://github.com/crystal-lang-tools/vscode-crystal-lang/issues/122) +- Use a separate task provider for shards (thanks @refi64) [#145](https://github.com/crystal-lang-tools/vscode-crystal-lang/pull/145) +- Stop the LSP when the extension is shutting down, where previously it would just stay on / active + +### Add + +- Ability to view and debug expanded source code from macros [#4](https://github.com/crystal-lang-tools/vscode-crystal-lang/issues/4) +- Add support for Jump to Definition [#42](https://github.com/crystal-lang-tools/vscode-crystal-lang/issues/42) +- Crystal support in Markdown code blocks [#56](https://github.com/crystal-lang-tools/vscode-crystal-lang/issues/56) +- Add Crystal Specs to native Testing UI (disabled by default for user-experience) [#163](https://github.com/crystal-lang-tools/vscode-crystal-lang/issues/163) +- Supporting mutiple main files (entry points) for a single project [#175](https://github.com/crystal-lang-tools/vscode-crystal-lang/issues/175) +- Add unused variable highlighting (thanks @grkek) [#155](https://github.com/crystal-lang-tools/vscode-crystal-lang/pull/155) +- make Crystal grammar compatible with TextMate (thanks @WhereIsX) [#150](https://github.com/crystal-lang-tools/vscode-crystal-lang/pull/150) +- GitHub Codespaces support (as long as Crystal is installed) +- Show problems in ECR files ## [0.6.0] - 2020-09-19 diff --git a/package-lock.json b/package-lock.json index 924872c..f414a0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,297 +1,1052 @@ { "name": "crystal-lang", - "version": "0.8.4", - "lockfileVersion": 1, + "version": "0.9.0-alpha.3", + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@types/mocha": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.3.tgz", - "integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==", + "packages": { + "": { + "name": "crystal-lang", + "version": "0.9.0-alpha.3", + "license": "MIT", + "dependencies": { + "async-mutex": "^0.4.0", + "junit2json": "~3.1.4", + "temp": "~0.9.4", + "vscode-languageclient": "^8.1.0", + "yaml": "^2.1.1" + }, + "devDependencies": { + "@types/express-serve-static-core": "^4.17.30", + "@types/node": "^14.11.1", + "@types/temp": "^0.9.4", + "@types/vscode": "^1.70.0", + "@vscode/vsce": "^2.22.0", + "esbuild": "^0.19.8", + "minimist": ">=1.2.2", + "prettier": "^2.8.4", + "typescript": "^4.0.3" + }, + "engines": { + "vscode": "^1.70.0" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.8.tgz", + "integrity": "sha512-31E2lxlGM1KEfivQl8Yf5aYU/mflz9g06H6S15ITUFQueMFtFjESRMoDSkvMo8thYvLBax+VKTPlpnx+sPicOA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.8.tgz", + "integrity": "sha512-B8JbS61bEunhfx8kasogFENgQfr/dIp+ggYXwTqdbMAgGDhRa3AaPpQMuQU0rNxDLECj6FhDzk1cF9WHMVwrtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.8.tgz", + "integrity": "sha512-rdqqYfRIn4jWOp+lzQttYMa2Xar3OK9Yt2fhOhzFXqg0rVWEfSclJvZq5fZslnz6ypHvVf3CT7qyf0A5pM682A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.8.tgz", + "integrity": "sha512-RQw9DemMbIq35Bprbboyf8SmOr4UXsRVxJ97LgB55VKKeJOOdvsIPy0nFyF2l8U+h4PtBx/1kRf0BelOYCiQcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.8.tgz", + "integrity": "sha512-3sur80OT9YdeZwIVgERAysAbwncom7b4bCI2XKLjMfPymTud7e/oY4y+ci1XVp5TfQp/bppn7xLw1n/oSQY3/Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.8.tgz", + "integrity": "sha512-WAnPJSDattvS/XtPCTj1tPoTxERjcTpH6HsMr6ujTT+X6rylVe8ggxk8pVxzf5U1wh5sPODpawNicF5ta/9Tmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.8.tgz", + "integrity": "sha512-ICvZyOplIjmmhjd6mxi+zxSdpPTKFfyPPQMQTK/w+8eNK6WV01AjIztJALDtwNNfFhfZLux0tZLC+U9nSyA5Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.8.tgz", + "integrity": "sha512-H4vmI5PYqSvosPaTJuEppU9oz1dq2A7Mr2vyg5TF9Ga+3+MGgBdGzcyBP7qK9MrwFQZlvNyJrvz6GuCaj3OukQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.8.tgz", + "integrity": "sha512-z1zMZivxDLHWnyGOctT9JP70h0beY54xDDDJt4VpTX+iwA77IFsE1vCXWmprajJGa+ZYSqkSbRQ4eyLCpCmiCQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.8.tgz", + "integrity": "sha512-1a8suQiFJmZz1khm/rDglOc8lavtzEMRo0v6WhPgxkrjcU0LkHj+TwBrALwoz/OtMExvsqbbMI0ChyelKabSvQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.8.tgz", + "integrity": "sha512-fHZWS2JJxnXt1uYJsDv9+b60WCc2RlvVAy1F76qOLtXRO+H4mjt3Tr6MJ5l7Q78X8KgCFudnTuiQRBhULUyBKQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.8.tgz", + "integrity": "sha512-Wy/z0EL5qZYLX66dVnEg9riiwls5IYnziwuju2oUiuxVc+/edvqXa04qNtbrs0Ukatg5HEzqT94Zs7J207dN5Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.8.tgz", + "integrity": "sha512-ETaW6245wK23YIEufhMQ3HSeHO7NgsLx8gygBVldRHKhOlD1oNeNy/P67mIh1zPn2Hr2HLieQrt6tWrVwuqrxg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.8.tgz", + "integrity": "sha512-T2DRQk55SgoleTP+DtPlMrxi/5r9AeFgkhkZ/B0ap99zmxtxdOixOMI570VjdRCs9pE4Wdkz7JYrsPvsl7eESg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.8.tgz", + "integrity": "sha512-NPxbdmmo3Bk7mbNeHmcCd7R7fptJaczPYBaELk6NcXxy7HLNyWwCyDJ/Xx+/YcNH7Im5dHdx9gZ5xIwyliQCbg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.8.tgz", + "integrity": "sha512-lytMAVOM3b1gPypL2TRmZ5rnXl7+6IIk8uB3eLsV1JwcizuolblXRrc5ShPrO9ls/b+RTp+E6gbsuLWHWi2zGg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.8.tgz", + "integrity": "sha512-hvWVo2VsXz/8NVt1UhLzxwAfo5sioj92uo0bCfLibB0xlOmimU/DeAEsQILlBQvkhrGjamP0/el5HU76HAitGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.8.tgz", + "integrity": "sha512-/7Y7u77rdvmGTxR83PgaSvSBJCC2L3Kb1M/+dmSIvRvQPXXCuC97QAwMugBNG0yGcbEGfFBH7ojPzAOxfGNkwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.8.tgz", + "integrity": "sha512-9Lc4s7Oi98GqFA4HzA/W2JHIYfnXbUYgekUP/Sm4BG9sfLjyv6GKKHKKVs83SMicBF2JwAX6A1PuOLMqpD001w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.8.tgz", + "integrity": "sha512-rq6WzBGjSzihI9deW3fC2Gqiak68+b7qo5/3kmB6Gvbh/NYPA0sJhrnp7wgV4bNwjqM+R2AApXGxMO7ZoGhIJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.8.tgz", + "integrity": "sha512-AIAbverbg5jMvJznYiGhrd3sumfwWs8572mIJL5NQjJa06P8KfCPWZQ0NwZbPQnbQi9OWSZhFVSUWjjIrn4hSw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.8.tgz", + "integrity": "sha512-bfZ0cQ1uZs2PqpulNL5j/3w+GDhP36k1K5c38QdQg+Swy51jFZWWeIkteNsufkQxp986wnqRRsb/bHbY1WQ7TA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.30", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz", + "integrity": "sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==" + }, + "node_modules/@types/qs": { + "version": "6.9.10", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", + "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==", "dev": true }, - "@types/node": { - "version": "14.11.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.1.tgz", - "integrity": "sha512-oTQgnd0hblfLsJ6BvJzzSL+Inogp3lq9fGgqRkMB/ziKMgEUaFl801OncOzUmalfzt14N0oPHMK47ipl+wbTIw==", + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, - "@types/vscode": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.49.0.tgz", - "integrity": "sha512-wfNQmLmm1VdMBr6iuNdprWmC1YdrgZ9dQzadv+l2eSjJlElOdJw8OTm4RU4oGTBcfvG6RZI2jOcppkdSS18mZw==", + "node_modules/@types/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@types/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-+VfWIwrlept2VBTj7Y2wQnI/Xfscy1u8Pyj/puYwss6V1IblXn1x7S0S9eFh6KyBolgLCm+rUFzhFAbdkR691g==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/vscode": { + "version": "1.84.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.84.0.tgz", + "integrity": "sha512-lCGOSrhT3cL+foUEqc8G1PVZxoDbiMmxgnUZZTEnHF4mC47eKAUtBGAuMLY6o6Ua8PAuNCoKXbqPmJd1JYnQfg==", "dev": true }, - "@types/yaml": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@types/yaml/-/yaml-1.9.7.tgz", - "integrity": "sha512-8WMXRDD1D+wCohjfslHDgICd2JtMATZU8CkhH8LVJqcJs6dyYj5TGptzP8wApbmEullGBSsCEzzap73DQ1HJaA==", + "node_modules/@types/xml2js": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.13.tgz", + "integrity": "sha512-nuT42GzgoUa4zZgBoF4d+Zqc12/FlVxXCT4xU6j3RfqTFVQWrUAClI/0sNJ5ImM9Wv6KB42KMG2xsVMn4cSBzA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vscode/vsce": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.22.0.tgz", + "integrity": "sha512-8df4uJiM3C6GZ2Sx/KilSKVxsetrTBBIUb3c0W4B1EWHcddioVs5mkyDKtMNP0khP/xBILVSzlXxhV+nm2rC9A==", "dev": true, - "requires": { - "yaml": "*" + "dependencies": { + "azure-devops-node-api": "^11.0.1", + "chalk": "^2.4.2", + "cheerio": "^1.0.0-rc.9", + "commander": "^6.2.1", + "glob": "^7.0.6", + "hosted-git-info": "^4.0.2", + "jsonc-parser": "^3.2.0", + "leven": "^3.1.0", + "markdown-it": "^12.3.2", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "semver": "^7.5.2", + "tmp": "^0.2.1", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.5.0", + "yauzl": "^2.3.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 14" + }, + "optionalDependencies": { + "keytar": "^7.7.0" } }, - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "node_modules/@vscode/vsce/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "requires": { - "es6-promisify": "^5.0.0" + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "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 + "node_modules/@vscode/vsce/node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } }, - "ansi-regex": { + "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "engines": { + "node": ">=8" + } }, - "ansi-styles": { + "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { + "dependencies": { "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { + "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "balanced-match": { + "node_modules/async-mutex": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.4.0.tgz", + "integrity": "sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/azure-devops-node-api": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz", + "integrity": "sha512-XdiGPhrpaT5J8wdERRKs5g8E0Zy1pvOYTli7z9E8nmOn3YGp4FhtjhrOyFmX/8veWCwdI69mCHKJw6l+4J/bHA==", + "dev": true, + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "optional": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, - "requires": { - "fill-range": "^7.0.1" + "engines": { + "node": "*" } }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true + "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" + } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/chalk/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, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "dependencies": { + "color-convert": "^1.9.0" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/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": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "color-name": "1.1.3" } }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "node_modules/chalk/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" } }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "color-convert": { + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "optional": true + }, + "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, - "requires": { + "dependencies": { "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "color-name": { + "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 + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } }, - "concat-map": { + "node_modules/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 + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true, - "requires": { - "ms": "2.1.2" + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "optional": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] }, - "emoji-regex": { + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "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 + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "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, + "optional": true, + "dependencies": { + "once": "^1.4.0" + } }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, - "requires": { - "es6-promise": "^4.0.3" + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.8.tgz", + "integrity": "sha512-l7iffQpT2OrZfH2rXIp7/FkmaeZM0vxbxN9KfiCwGYuZqzMg/JdvX26R31Zxn/Pxvsrg3Y9N6XTcnknqDyyv4w==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.19.8", + "@esbuild/android-arm64": "0.19.8", + "@esbuild/android-x64": "0.19.8", + "@esbuild/darwin-arm64": "0.19.8", + "@esbuild/darwin-x64": "0.19.8", + "@esbuild/freebsd-arm64": "0.19.8", + "@esbuild/freebsd-x64": "0.19.8", + "@esbuild/linux-arm": "0.19.8", + "@esbuild/linux-arm64": "0.19.8", + "@esbuild/linux-ia32": "0.19.8", + "@esbuild/linux-loong64": "0.19.8", + "@esbuild/linux-mips64el": "0.19.8", + "@esbuild/linux-ppc64": "0.19.8", + "@esbuild/linux-riscv64": "0.19.8", + "@esbuild/linux-s390x": "0.19.8", + "@esbuild/linux-x64": "0.19.8", + "@esbuild/netbsd-x64": "0.19.8", + "@esbuild/openbsd-x64": "0.19.8", + "@esbuild/sunos-x64": "0.19.8", + "@esbuild/win32-arm64": "0.19.8", + "@esbuild/win32-ia32": "0.19.8", + "@esbuild/win32-x64": "0.19.8" } }, - "escalade": { + "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "engines": { + "node": ">=6" + } }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true + "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": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } }, - "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==", + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", "dev": true, - "requires": { - "to-regex-range": "^5.0.1" + "optional": true, + "engines": { + "node": ">=6" } }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "dependencies": { + "pend": "~1.2.0" } }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "optional": true }, - "fs.realpath": { + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, - "optional": true + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "get-caller-file": { + "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", "dev": true, - "requires": { + "optional": true + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", @@ -299,579 +1054,1051 @@ "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, - "requires": { - "is-glob": "^4.0.1" + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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 + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "dev": true, - "requires": { - "agent-base": "4", - "debug": "3.1.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" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" } }, - "inflight": { + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "optional": true }, - "is-fullwidth-code-point": { + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/junit2json": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/junit2json/-/junit2json-3.1.4.tgz", + "integrity": "sha512-56vEmCjFcLfsDVdBfhyBXJhs3TXL5gKGVr5cbkyoddRlBBq311iQIsupgelo2eBooAZ183VcgHAfvRZH2ZEg6Q==", + "dependencies": { + "@types/xml2js": "0.4.13", + "xml2js": "0.6.2", + "yargs": "17.7.2" + }, + "bin": { + "junit2json": "dist/cjs/cli.js" + } + }, + "node_modules/junit2json/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/junit2json/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/junit2json/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", "dev": true, - "requires": { - "is-extglob": "^2.1.1" + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" } }, - "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 + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dev": true, + "dependencies": { + "uc.micro": "^1.0.1" + } }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", "dev": true, - "requires": { - "argparse": "^2.0.1" + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" } }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/markdown-it/node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", "dev": true, - "requires": { - "p-locate": "^5.0.0" + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" } }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "requires": { + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { "brace-expansion": "^2.0.1" }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - } + "balanced-match": "^1.0.0" } }, - "minimist": { + "node_modules/minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, - "mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, - "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/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "optional": true }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "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 + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true, + "optional": true + }, + "node_modules/node-abi": { + "version": "3.51.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.51.0.tgz", + "integrity": "sha512-SQkEP4hmNWjlniS5zdnfIXTk1x7Ome85RDzHlTbBtzE97Gfwz/Ipw4v/Ryk20DWIy3yCNVLVlGKApCnmvYoJbA==", + "dev": true, + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true, + "optional": true + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { + "dependencies": { "wrappy": "1" } }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==", + "dev": true, + "dependencies": { + "semver": "^5.1.0" + } + }, + "node_modules/parse-semver/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "requires": { - "yocto-queue": "^0.1.0" + "bin": { + "semver": "bin/semver" } }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", "dev": true, - "requires": { - "p-limit": "^3.0.2" + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "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 + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dev": true, + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } }, - "path-is-absolute": { + "node_modules/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 + "engines": { + "node": ">=0.10.0" + } }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dev": true, + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "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, + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", "dev": true, - "requires": { - "safe-buffer": "^5.1.0" + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" } }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", "dev": true, - "requires": { - "picomatch": "^2.2.1" + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true + "engines": { + "node": ">=0.10.0" + } }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { + "node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dependencies": { "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" } }, - "safe-buffer": { + "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true }, - "semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "requires": { + "node_modules/sax": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", "dev": true, - "requires": { - "randombytes": "^2.1.0" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" } }, - "string-width": { + "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { + "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-ansi": { + "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { + "dependencies": { "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "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, - "requires": { - "has-flag": "^4.0.0" + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "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==", + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "optional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "optional": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", + "dependencies": { + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/tmp/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", + "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==", + "dev": true, + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, - "requires": { - "is-number": "^7.0.0" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" } }, - "typescript": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz", - "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==", + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "dev": true + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, - "vscode-jsonrpc": { + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "optional": true + }, + "node_modules/vscode-jsonrpc": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz", - "integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==" + "integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==", + "engines": { + "node": ">=14.0.0" + } }, - "vscode-languageclient": { + "node_modules/vscode-languageclient": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.1.0.tgz", "integrity": "sha512-GL4QdbYUF/XxQlAsvYWZRV3V34kOkpRlvV60/72ghHfsYFnS/v2MANZ9P6sHmxFcZKOse8O+L9G7Czg0NUWing==", - "requires": { + "dependencies": { "minimatch": "^5.1.0", "semver": "^7.3.7", "vscode-languageserver-protocol": "3.17.3" }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "requires": { - "brace-expansion": "^2.0.1" - } - } + "engines": { + "vscode": "^1.67.0" } }, - "vscode-languageserver-protocol": { + "node_modules/vscode-languageserver-protocol": { "version": "3.17.3", "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz", "integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==", - "requires": { + "dependencies": { "vscode-jsonrpc": "8.1.0", "vscode-languageserver-types": "3.17.3" } }, - "vscode-languageserver-types": { + "node_modules/vscode-languageserver-types": { "version": "3.17.3", "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz", "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==" }, - "vscode-test": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.4.0.tgz", - "integrity": "sha512-Jt7HNGvSE0+++Tvtq5wc4hiXLIr2OjDShz/gbAfM/mahQpy4rKBnmOK33D+MR67ATWviQhl+vpmU3p/qwSH/Pg==", - "dev": true, - "requires": { - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.4", - "rimraf": "^2.6.3" - } - }, - "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, - "wrap-ansi": { + "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true + "engines": { + "node": ">=10" + } }, - "yallist": { + "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "yaml": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", - "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==" + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "engines": { + "node": ">= 14" + } }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" } }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" + "dependencies": { + "buffer-crc32": "~0.2.3" } - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true } } } diff --git a/package.json b/package.json index 6e25cb3..f5bb9cc 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "crystal-lang", "displayName": "Crystal Language", "description": "The Crystal Programming Language", - "version": "0.8.4", + "version": "0.9.0", "publisher": "crystal-lang-tools", "icon": "images/icon.gif", "license": "MIT", @@ -12,7 +12,7 @@ }, "bugs": "https://github.com/crystal-lang-tools/vscode-crystal-lang/issues", "engines": { - "vscode": "^1.49.0" + "vscode": "^1.70.0" }, "keywords": [ "crystal", @@ -30,10 +30,11 @@ "color": "#000000", "theme": "dark" }, + "main": "./out/main.js", "activationEvents": [ - "onLanguage:crystal" + "workspaceContains:*/shard.yml", + "workspaceContains:*/shard.override.yml" ], - "main": "./out/src/crystalMain", "contributes": { "languages": [ { @@ -50,7 +51,7 @@ { "id": "ecr", "aliases": [ - "Embed Crystal" + "Embedded Crystal" ], "extensions": [ ".ecr" @@ -113,12 +114,6 @@ "path": "./snippets/slang.json" } ], - "keybindings": [ - { - "command": "editor.action.formatDocument", - "key": "Ctrl+Shift+I" - } - ], "taskDefinitions": [ { "type": "crystal", @@ -156,98 +151,109 @@ "type": "object", "title": "Crystal Lang configuration", "properties": { - "crystal-lang.problems": { - "type": "string", - "default": "syntax", - "enum": [ - "syntax", - "build", - "none" - ], - "description": "Allow to check errors.\n[syntax] show parser errors\n[build] compile without codegen\n[none] disable check." - }, - "crystal-lang.implementations": { - "type": "boolean", - "default": false, - "description": "Allow to search implementation for method under cursor." - }, - "crystal-lang.hover": { - "type": "boolean", - "default": false, - "description": "Show symbol information when mouse is hover." - }, - "crystal-lang.completion": { - "type": "boolean", - "default": false, - "description": "Basic completion of methods and symbols." - }, - "crystal-lang.mainFile": { - "type": "string", - "default": "", - "description": "Check main file instead of current file.\nBy example: ${workspaceRoot}/src/main.cr." - }, - "crystal-lang.maxNumberOfProblems": { - "type": "number", - "default": 100, - "description": "Controls the maximum number of problems." - }, - "crystal-lang.processesLimit": { - "type": "number", - "default": 3, - "description": "[Reload required]\nCrystal processes running in background." - }, "crystal-lang.compiler": { + "title": "Compiler", "type": "string", "default": "crystal", - "description": "Absolute path for Crystal compiler executable." + "description": "The path to the Crystal compiler." }, "crystal-lang.shards": { + "title": "Shards", "type": "string", "default": "shards", - "description": "Absolute path for Shards compiler executable." + "description": "The path to the Shards executable." }, "crystal-lang.server": { "type": "string", "default": "", - "description": "[Experimental][Reload required]\nAbsolute path for Scry server binary\n(Language Server Protocol for Crystal)." + "description": "[Experimental][Reload required]\nAbsolute path for Scry/Crystalline LSP server binary\n(Language Server Protocol for Crystal)." }, - "crystal-lang.logLevel": { + "crystal-lang.main": { "type": "string", - "default": "error", - "enum": [ - "debug", - "info", - "warn", - "error", - "fatal" - ], - "description": "Controls the amount of data logged by crystal-lang.server" + "default": null, + "description": "Specify the main file for the tools.\nDoes not work with multi-root workspaces." }, - "crystal-lang.bashOnWindows": { + "crystal-lang.spec-explorer": { "type": "boolean", "default": false, - "description": "[Reload required]\nEnables support for Windows Subsystem Linux." + "description": "[Reload required]\nShow specs in the Test Explorer." + }, + "crystal-lang.spec-tags": { + "type": "string", + "default": "", + "description": "Tags to pass to the spec runner, i.e. '--tag hello --tag ~world'" + }, + "crystal-lang.hover": { + "type": "boolean", + "default": true, + "description": "[Reload required]\nShow type information on hover." + }, + "crystal-lang.definitions": { + "type": "boolean", + "default": true, + "description": "[Reload required]\nEnables jump to definition." + }, + "crystal-lang.problems": { + "type": "boolean", + "default": true, + "description": "[Reload required]\nEnable problems finder. Runs the Crystal compiler on save and reports any problems found." + }, + "crystal-lang.dependencies": { + "type": "boolean", + "default": true, + "description": "Use the dependencies tool to determine main for each file. Can be slow." + }, + "crystal-lang.flags": { + "type": "string", + "default": "", + "description": "Compile-time flags to pass to the compiler, i.e. '-Dflag1 -Dflag2'.\nFlags are shared across multi-root workspaces." } } + }, + "commands": [ + { + "command": "crystal-lang.showMacroExpansion", + "title": "Show macro expansion" + } + ], + "menus": { + "editor/context": [ + { + "command": "crystal-lang.showMacroExpansion", + "group": "z_commands" + } + ] } }, "scripts": { - "vscode:prepublish": "tsc -p ./", - "compile": "tsc -watch -p ./", - "test": "echo ok" + "format": "prettier --write ./src/**/*.ts", + "package": "vsce package", + "vscode:prepublish": "npm run esbuild-base -- --minify", + "esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node", + "esbuild": "npm run esbuild-base -- --sourcemap", + "esbuild-watch": "npm run esbuild-base -- --sourcemap --watch", + "test-compile": "tsc -p ./" + }, + "dependencies": { + "async-mutex": "^0.4.0", + "junit2json": "~3.1.4", + "temp": "~0.9.4", + "vscode-languageclient": "^8.1.0", + "yaml": "^2.1.1" }, "devDependencies": { - "@types/mocha": "^8.0.3", + "@types/express-serve-static-core": "^4.17.30", "@types/node": "^14.11.1", - "@types/vscode": "^1.49.0", - "@types/yaml": "^1.9.7", - "minimist": ">=1.2.6", - "mocha": "^10.2.0", - "typescript": "^4.0.3", - "vscode-test": "^1.4.0" + "@types/temp": "^0.9.4", + "@types/vscode": "^1.70.0", + "@vscode/vsce": "^2.22.0", + "esbuild": "^0.19.8", + "minimist": ">=1.2.2", + "prettier": "^2.8.4", + "typescript": "^4.0.3" }, - "dependencies": { - "vscode-languageclient": "^8.1.0", - "yaml": "^1.9.7" + "prettier": { + "arrowParens": "avoid", + "singleQuote": true } } diff --git a/src/completion.ts b/src/completion.ts new file mode 100644 index 0000000..79a1923 --- /dev/null +++ b/src/completion.ts @@ -0,0 +1,130 @@ +import { + CancellationToken, + CompletionContext, + CompletionItem, + CompletionItemKind, + CompletionItemProvider, + CompletionList, + DocumentSelector, + ExtensionContext, + languages, + Position, + Range, + SymbolKind, + TextDocument, +} from 'vscode'; +import globals from './definitions/globals'; +import methods from './definitions/methods'; + +class CrystalCompletionItemProvider implements CompletionItemProvider { + private completions: CompletionItem[]; + + async provideCompletionItems( + document: TextDocument, + position: Position, + token: CancellationToken, + context: CompletionContext + ): Promise> { + this.completions = []; + + // TODO: These should be added where types or classes are appropriate + if (context.triggerCharacter == " ") { + this.push(globals.CLASSES, SymbolKind.Class); + this.push(globals.MODULES, SymbolKind.Module); + this.push(globals.STRUCTS, SymbolKind.Struct); + } + + const line = document.lineAt(position.line); + if (!line || /^#(?!{).+/.test(line.text)) return []; + + const column = new Position( + position.line, + position.character > 2 ? position.character - 2 : 0 + ); + const dot = new Position(position.line, column.character + 1); + const range = + document.getWordRangeAtPosition(dot) || + document.getWordRangeAtPosition(column); + // TODO: check symbols + + if (range) { + let text = document.getText( + new Range( + range.start.line, + range.start.character, + range.end.line, + range.end.character + 1 + ) + ); + + if (text.endsWith('.')) { + this.push(methods.PSEUDO, SymbolKind.Method); + let isStatic = false; + } else { + let text = document.getText( + new Range( + range.start.line, + range.start.character, + range.end.line, + range.end.character + 2 + ) + ); + // TODO: handle :: + } + } else { + this.push(methods.TOP_LEVEL, SymbolKind.Method); + } + + return this.completions; + } + + private push(data: string[][], kind: SymbolKind): void { + data.forEach(d => this.create(d, kind)); + } + + private create(data: string[], kind: SymbolKind): void { + const comp = new CompletionItem(data[0], this.getKind(kind)); + comp.detail = data[1]; + comp.documentation = data[2]; + comp.sortText = ('0000' + this.completions.length).slice(-4); + this.completions.push(comp); + } + + private getKind(kind: SymbolKind): CompletionItemKind { + switch (kind) { + case SymbolKind.Module: + return CompletionItemKind.Module; + case SymbolKind.Class: + return CompletionItemKind.Class; + case SymbolKind.Struct: + return CompletionItemKind.Struct; + case SymbolKind.Constant: + return CompletionItemKind.Constant; + case SymbolKind.Enum: + return CompletionItemKind.Enum; + case SymbolKind.Function: + return CompletionItemKind.Function; + case SymbolKind.Property: + return CompletionItemKind.Property; + case SymbolKind.Method: + return CompletionItemKind.Method; + case SymbolKind.Variable: + return CompletionItemKind.Variable; + } + } +} + +export function registerCompletion( + selector: DocumentSelector, + context: ExtensionContext +): void { + context.subscriptions.push( + languages.registerCompletionItemProvider( + selector, + new CrystalCompletionItemProvider(), + '.', + '::', + ' ' + ) + ); +} diff --git a/src/crystalCompletion.ts b/src/crystalCompletion.ts deleted file mode 100755 index 5703f0d..0000000 --- a/src/crystalCompletion.ts +++ /dev/null @@ -1,254 +0,0 @@ -import * as vscode from "vscode" -import * as TDATA from "./crystalCompletionData" - -import { CrystalContext } from "./crystalContext" -import { getSymbols } from "./crystalUtils" - -/** - * Completion provider using VSCode module - */ -export class CrystalCompletionItemProvider extends CrystalContext implements vscode.CompletionItemProvider { - - // Completions attribute - private completions: vscode.CompletionList - - /** - * Convert VSCode completion items to symbols - */ - private getItemKindFromSymbolKind(kind) { - switch (kind) { - case 4: - return vscode.CompletionItemKind.Class - case 11: - return vscode.CompletionItemKind.Function - case 6: - return vscode.CompletionItemKind.Property - case 22: - return vscode.CompletionItemKind.Struct - case 1: - return vscode.CompletionItemKind.Module - case 9: - return vscode.CompletionItemKind.Enum - case 13: - return vscode.CompletionItemKind.Constant - case 12: - return vscode.CompletionItemKind.Variable - case 5: - return vscode.CompletionItemKind.Method - default: - return 0 - } - } - - /** - * Add a method to completion list - */ - private pushCompletionMethods(completions) { - for (let method of completions) { - this.createCompletionItem(method[0], method[1], method[2], vscode.SymbolKind.Method) - } - } - - /** - * Add a symbols to completion list (needed by symbol provider) - */ - private pushCompletionOther(completions, kind) { - for (let completion of completions) { - this.createCompletionItem(completion[0], completion[1], completion[2], kind) - } - } - - /** - * Create a new completion item - */ - private createCompletionItem(name, detail, documentation, kind) { - let completion = new vscode.CompletionItem(`${name}`, this.getItemKindFromSymbolKind(kind)) - completion.documentation = documentation - completion.detail = detail - completion.sortText = ("0000" + this.completions.items.length).slice(-4) // <-- from vscode-nim - this.completions.items.push(completion) - } - - /** - * Return completion data to VSCode - */ - async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) { - // ----------------------------------------------- - // TODO: improve Type completion algorithm (again) - // ----------------------------------------------- - this.completions = new vscode.CompletionList - const config = vscode.workspace.getConfiguration("crystal-lang") - if (!config["completion"]) { - return this.completions - } - let line = document.getText(new vscode.Range(position.line, 0, position.line, position.character)) - if (!line) { - return this.completions - } - let comment = line.match(/^[^\"]*#[^\{].*$/) - let quotes = line.match(/(\")/g) - if (quotes || comment) { // Check if line isn't a comment or string - return this.completions - } - let column = (position.character > 2) ? (position.character - 2) : 0 // Remove dot or colons and search for a word - let posDot = new vscode.Position(position.line, column + 1) - let posColons = new vscode.Position(position.line, column) - let wordRange = document.getWordRangeAtPosition(posDot) || document.getWordRangeAtPosition(posColons) - let symbols = await getSymbols(document.uri) - let completionFlag = false - if (wordRange) { - // Check if a call token (.|::) following the word for a method call - let range = new vscode.Range(wordRange.start.line, wordRange.start.character, wordRange.end.line, wordRange.end.character + 1) - var word = document.getText(range) - if (word.endsWith(".")) { - completionFlag = true - let container = word.slice(0, -1) - this.pushCompletionMethods(TDATA.REFLECTION_METHODS) // Add Type reflection - let symbolsNames = symbols.map((sym) => { return sym.name }) // Create symbols index array - let staticFound = false // Check static methods for class completion - let containerIndex = symbolsNames.indexOf(container) - if (containerIndex >= 0) { - let containerSymbol = symbols[containerIndex] - for (let symbol of symbols) { - if (symbol.name.startsWith("self") && symbol.containerName == containerSymbol.name) { - staticFound = true - this.createCompletionItem(symbol.name.slice(5), symbol.containerName, `Belongs to ${word}`, symbol.kind) - } - } - let symbol = symbols[containerIndex] - if (symbol.kind == vscode.SymbolKind.Class || symbol.kind == vscode.SymbolKind.Struct) { - staticFound = true - this.createCompletionItem("new", "new(*args)", `Create a new instance of an Object`, vscode.SymbolKind.Method) - } - } - // ---------------------------------------- - // TODO: Add standard lib method completion - // ---------------------------------------- - if (!staticFound) { - if (container == "File") { - this.pushCompletionMethods(TDATA.FILE_METHODS) - staticFound = true - } else if (container == "Dir") { - this.pushCompletionMethods(TDATA.DIR_METHODS) - staticFound = true - } - } - if (!staticFound) { // Add instance methods to variables - let crystalOutput = await this.crystalContext(document, position, "completion") - if (crystalOutput.toString().startsWith(`{"status":"`)) { - try { - let crystalMessageObject = JSON.parse(crystalOutput.toString()) - if (crystalMessageObject.status == "ok") { - for (let context of crystalMessageObject.contexts) { - let type = context[container] - if (type) { - if (type.endsWith("Nil")) { - this.pushCompletionMethods(TDATA.NIL_METHODS) - } else if (type == "Bool") { - this.pushCompletionMethods(TDATA.BOOL_METHODS) - } else if (type.startsWith("Int") || type.startsWith("UInt")) { - this.pushCompletionMethods(TDATA.INT_METHODS) - } else if (type == "Float32" || type == "Float64") { - this.pushCompletionMethods(TDATA.FLOAT_METHODS) - } else if (type == "Char") { - this.pushCompletionMethods(TDATA.CHAR_METHODS) - } else if (type == "String") { - this.pushCompletionMethods(TDATA.STRING_METHODS) - } else if (type == "Symbol") { - this.pushCompletionMethods(TDATA.SYMBOLS_METHODS) - } else if (type.startsWith("Array")) { - this.pushCompletionMethods(TDATA.ARRAY_METHODS) - } else if (type.startsWith("Hash")) { - this.pushCompletionMethods(TDATA.HASH_METHODS) - } else if (type.startsWith("Range")) { - this.pushCompletionMethods(TDATA.RANGE_METHODS) - } else if (type == "Regex") { - this.pushCompletionMethods(TDATA.REGEX_METHODS) - } else if (type.startsWith("Tuple")) { - this.pushCompletionMethods(TDATA.TUPLE_METHODS) - } else if (type.startsWith("NamedTuple")) { - this.pushCompletionMethods(TDATA.NAMEDTUPLE_METHODS) - } else if (type.startsWith("Proc")) { - this.pushCompletionMethods(TDATA.PROC_METHODS) - } else if (type.startsWith("Channel")) { - this.pushCompletionMethods(TDATA.CHANNEL_METHODS) - } - // Complete instance methods of Type - // class String - // def tortilla? - // self == "tortilla" - // end - // end - // a = "arepas" - // a.tortilla? - let types = type.split("::") - let symbolType = types.pop() - for (let symbol of symbols) { - if (symbol.containerName == symbolType && - !symbol.name.startsWith("self.") && - !(symbol.name == "initialize") && - symbol.kind == vscode.SymbolKind.Function) { - this.createCompletionItem(symbol.name, symbol.containerName, `Instance method of ${type}`, symbol.kind) - } - } - break - } - } - } - } catch (err) { - console.error("ERROR: JSON.parse failed to parse crystal context output when completion") - throw err - } - } - } - } else { - range = new vscode.Range(wordRange.start.line, wordRange.start.character, wordRange.end.line, wordRange.end.character + 2) - word = document.getText(range) - if (word.endsWith("::")) { - completionFlag = true - let container = word.slice(0, -2) - // ------------------------------------------ - // TODO: Add standard lib subtypes completion - // ------------------------------------------ - // if (container == "Char") { - // this.pushCompletionOther(TDATA.CHAR_SUBTYPES, vscode.SymbolKind.Struct) - // } - // Add SubTypes completion - for (let symbol of symbols) { - if (symbol.containerName == container && symbol.kind != vscode.SymbolKind.Function) { - this.createCompletionItem(symbol.name, symbol.containerName, `Belongs to ${container}`, symbol.kind) - } - } - } else if (word.endsWith(").")) { - completionFlag = true - // --------------------------------------------------------- - // TODO: Do something with chain methods and generic Classes - // --------------------------------------------------------- - } - } - } - if (!completionFlag) { - for (let symbol of symbols) { // Complete document symbols - if (word) { - if (symbol.name.startsWith(word.replace(")", ""))) { - this.createCompletionItem(symbol.name, symbol.containerName, "", symbol.kind) - } - } else { - this.createCompletionItem(symbol.name, symbol.containerName, "", symbol.kind) - } - } - // Complete Top Level Methods - this.pushCompletionMethods(TDATA.TOP_LEVEL_METHODS) - // Complete Standard lib - // ------------------------------------------ - // TODO: Add standard lib types documentation - // ------------------------------------------ - this.pushCompletionOther(TDATA.STRUCTS, vscode.SymbolKind.Struct) - this.pushCompletionOther(TDATA.MODULES, vscode.SymbolKind.Module) - this.pushCompletionOther(TDATA.CLASSES, vscode.SymbolKind.Class) - this.pushCompletionOther(TDATA.ENUMS, vscode.SymbolKind.Enum) - this.pushCompletionOther(TDATA.ALIAS, vscode.SymbolKind.Constant) - } - return this.completions - } -} diff --git a/src/crystalCompletionData.ts b/src/crystalCompletionData.ts deleted file mode 100644 index fbb8d78..0000000 --- a/src/crystalCompletionData.ts +++ /dev/null @@ -1,1159 +0,0 @@ -// Data for completion taken from crystal source -// Extracted by @faustinoaq using VSCode -// ------------------------------------------ -// TODO: Include all the Standard Library ;-) -// ------------------------------------------ - -export const REFLECTION_METHODS = [ - ["is_a?", "is_a?", "The pseudo-method is_a? determines whether an expression's runtime type inherits or includes another type."], - ["nil?", "nil?", "The pseudo-method nil? determines whether an expression's runtime is Nil."], - ["responds_to?", "responds_to?", "The pseudo-method responds_to? determines whether a type has a method with the given name."], - ["as", "as", "The as pseudo-method restricts the types of an expression."], - ["as?", "as?", "The as? pseudo-method is similar to as, except that it returns nil instead of raising an exception when the type doesn't match. It also can't be used to cast between pointer types and other types.", 5] -] -export const NIL_METHODS = [ - ["clone", "clone", ""], - ["to_json", "to_json(json : JSON::Builder)", ""], - ["==", "==(other : Nil)", "Returns true: Nil has only one singleton value: nil."], - ["hash", "hash", "Returns 0."], - ["inspect", "inspect(io)", "Writes nil to the given IO."], - ["inspect", "inspect", "Returns nil."], - ["not_nil!", "not_nil!", "Raises an exception."], - ["object_id", "object_id", "Returns 0_u64."], - ["same?", "same?(other : Nil)", "Returns true: Nil has only one singleton value: nil."], - ["same?", "same?(other : Reference)", "Returns false."], - ["to_s", "to_s(io : IO)", "Doesn't write anything to the given IO."], - ["to_s", "to_s", "Returns an empty string."], - ["to_yaml", "to_yaml(yaml : YAML::Builder)", ""], - ["try", "try(&block)", "Doesn't yields to the block.", 5] -] -export const BOOL_METHODS = [ - ["!=", "!=(other : Bool) : Bool", "Returns true if self is not equal to other."], - ["&", "&(other : Bool)", "Bitwise AND."], - ["==", "==(other : Bool) : Bool", "Returns true if self is equal to other."], - ["^", "^(other : Bool)", "Exclusive OR."], - ["hash", "hash", "Returns a hash value for this boolean: 0 for false, 1 for true."], - ["to_s", "to_s(io)", "Appends true for true and false for false to the given IO."], - ["|", "|(other : Bool)", "Bitwise OR."], - ["to_s", "to_s", "Returns true for true and false for false."], - ["clone", "clone", ""], - ["to_json", "to_json(json : JSON::Builder)", ""], - ["to_yaml", "to_yaml(yaml : YAML::Builder)", "", 5] -] -export const INT_METHODS = [ - ["%", "%(other : Int)", "Returns self modulo other."], - ["**", "**(exponent : Float) : Float64", "Returns the value of raising self to the power of exponent."], - ["**", "**(exponent : Int) : self", "Returns the value of raising self to the power of exponent."], - ["/", "/(other : Int)", "Divides self by other using floored division."], - ["<<", "<<(count : Int)", "Returns the result of shifting this number's bits count positions to the left."], - [">>", ">>(count : Int)", "Returns the result of shifting this number's bits count positions to the right."], - ["bit", "bit(bit)", "Returns this number's bitth bit, starting with the least-significant."], - ["chr", "chr", "Returns a Char that has the unicode codepoint of self."], - ["popcount", "popcount", "Counts 1-bits in the binary representation of this integer."], - ["remainder", "remainder(other : Int)", "Returns self remainder other."], - ["tdiv", "tdiv(other : Int)", "Divides self by other using truncated division."], - ["to_big_i", "to_big_i : BigInt", "Returns a BigInt representing this integer."], - ["to_big_r", "to_big_r", "Returns a BigRational representing this integer."], - ["to_io", "to_io(io : IO, format : IO::ByteFormat)", "Writes this integer to the given io in the given format."], - ["%", "%(other : BigInt) : BigInt", ""], - ["*", "*(other : BigRational)", ""], - ["*", "*(other : BigInt) : BigInt", ""], - ["+", "+(other : BigRational)", ""], - ["+", "+(other : BigInt) : BigInt", ""], - ["-", "-(other : BigRational)", ""], - ["-", "-(other : BigInt) : BigInt", ""], - ["/", "/(other : BigRational)", ""], - ["/", "/(other : BigInt) : BigInt", ""], - ["<=>", "<=>(other : BigInt)", ""], - ["<=>", "<=>(other : BigRational)", ""], - ["===", "===(char : Char)", ""], - ["abs", "abs", "Return absolute value of a number."], - ["ceil", "ceil", ""], - ["day", "day", ""], - ["days", "days", ""], - ["divisible_by?", "divisible_by?(num)", ""], - ["downto", "downto(to)", ""], - ["downto", "downto(to, &block : self -> ) : Nil", ""], - ["even?", "even?", ""], - ["fdiv", "fdiv(other)", ""], - ["floor", "floor", ""], - ["gcd", "gcd(other : Int)", ""], - ["gcm", "gcm(other : BigInt) : Int", ""], - ["hash", "hash", ""], - ["hour", "hour", ""], - ["hours", "hours", ""], - ["lcm", "lcm(other : Int)", ""], - ["lcm", "lcm(other : BigInt) : BigInt", ""], - ["millisecond", "millisecond", ""], - ["milliseconds", "milliseconds", ""], - ["minute", "minute", ""], - ["minutes", "minutes", ""], - ["modulo", "modulo(other)", ""], - ["month", "month", ""], - ["months", "months", ""], - ["odd?", "odd?", ""], - ["pred", "pred", ""], - ["round", "round", ""], - ["second", "second", ""], - ["seconds", "seconds", ""], - ["succ", "succ", ""], - ["times", "times(&block : self -> ) : Nil", ""], - ["times", "times", ""], - ["to", "to(to)", ""], - ["to", "to(to, &block : self -> ) : Nil", ""], - ["to_json", "to_json(json : JSON::Builder)", ""], - ["to_s", "to_s(base : Int, upcase ':' Bool = false)", ""], - ["to_s", "to_s(io : IO)", ""], - ["to_s", "to_s(base : Int, io : IO, upcase : Bool = false)", ""], - ["to_s", "to_s", ""], - ["trunc", "trunc", ""], - ["upto", "upto(to)", ""], - ["upto", "upto(to, &block : self -> ) : Nil", ""], - ["week", "week", ""], - ["weeks", "weeks", ""], - ["year", "year", ""], - ["years", "years", ""], - ["~", "~", ""], - ["popcount", "popcount", ""], - ["clone", "clone", ""], - ["!=", "!=(other : Int16) : Bool", "Returns true if self is not equal to other."], - ["!=", "!=(other : Int8) : Bool", "Returns true if self is not equal to other."], - ["!=", "!=(other : Int32) : Bool", "Returns true if self is not equal to other."], - ["!=", "!=(other : Float64) : Bool", "Returns true if self is not equal to other."], - ["!=", "!=(other : Float32) : Bool", "Returns true if self is not equal to other."], - ["!=", "!=(other : UInt64) : Bool", "Returns true if self is not equal to other."], - ["!=", "!=(other : UInt32) : Bool", "Returns true if self is not equal to other."], - ["!=", "!=(other : UInt16) : Bool", "Returns true if self is not equal to other."], - ["!=", "!=(other : UInt8) : Bool", "Returns true if self is not equal to other."], - ["!=", "!=(other : Int64) : Bool", "Returns true if self is not equal to other."], - ["&", "&(other : Int8) : self", "Returns the result of performing a bitwise AND of self's and other's bits."], - ["&", "&(other : Int16) : self", "Returns the result of performing a bitwise AND of self's and other's bits."], - ["&", "&(other : Int32) : self", "Returns the result of performing a bitwise AND of self's and other's bits."], - ["&", "&(other : Int64) : self", "Returns the result of performing a bitwise AND of self's and other's bits."], - ["&", "&(other : UInt8) : self", "Returns the result of performing a bitwise AND of self's and other's bits."], - ["&", "&(other : UInt16) : self", "Returns the result of performing a bitwise AND of self's and other's bits."], - ["&", "&(other : UInt32) : self", "Returns the result of performing a bitwise AND of self's and other's bits."], - ["&", "&(other : UInt64) : self", "Returns the result of performing a bitwise AND of self's and other's bits."], - ["*", "*(other : Int8) : self", "Returns the result of multiplying self and other."], - ["*", "*(other : Int16) : self", "Returns the result of multiplying self and other."], - ["*", "*(other : Int32) : self", "Returns the result of multiplying self and other."], - ["*", "*(other : Int64) : self", "Returns the result of multiplying self and other."], - ["*", "*(other : UInt16) : self", "Returns the result of multiplying self and other."], - ["*", "*(other : UInt32) : self", "Returns the result of multiplying self and other."], - ["*", "*(other : UInt64) : self", "Returns the result of multiplying self and other."], - ["*", "*(other : Float32) : Float32", "Returns the result of multiplying self and other."], - ["*", "*(other : Float64) : Float64", "Returns the result of multiplying self and other."], - ["*", "*(other : UInt8) : self", "Returns the result of multiplying self and other."], - ["+", "+(other : Int16) : self", "Returns the result of adding self and other."], - ["+", "+(other : Int32) : self", "Returns the result of adding self and other."], - ["+", "+(other : Int64) : self", "Returns the result of adding self and other."], - ["+", "+(other : Float64) : Float64", "Returns the result of adding self and other."], - ["+", "+(other : Float32) : Float32", "Returns the result of adding self and other."], - ["+", "+(other : UInt64) : self", "Returns the result of adding self and other."], - ["+", "+(other : UInt32) : self", "Returns the result of adding self and other."], - ["+", "+(other : UInt16) : self", "Returns the result of adding self and other."], - ["+", "+(other : UInt8) : self", "Returns the result of adding self and other."], - ["+", "+(other : Int8) : self", "Returns the result of adding self and other."], - ["-", "-(other : Int8) : self", "Returns the result of subtracting self and other."], - ["-", "-(other : Float64) : Float64", "Returns the result of subtracting self and other."], - ["-", "-(other : Float32) : Float32", "Returns the result of subtracting self and other."], - ["-", "-(other : UInt64) : self", "Returns the result of subtracting self and other."], - ["-", "-(other : UInt32) : self", "Returns the result of subtracting self and other."], - ["-", "-(other : UInt16) : self", "Returns the result of subtracting self and other."], - ["-", "-(other : UInt8) : self", "Returns the result of subtracting self and other."], - ["-", "-(other : Int64) : self", "Returns the result of subtracting self and other."], - ["-", "-(other : Int32) : self", "Returns the result of subtracting self and other."], - ["-", "-(other : Int16) : self", "Returns the result of subtracting self and other."], - ["/", "/(other : Float32) : Float32", "Returns the result of dividing self and other."], - ["/", "/(other : Float64) : Float64", "Returns the result of dividing self and other."], - ["<", "<(other : UInt16) : Bool", "Returns true if self is less than other."], - ["<", "<(other : Int32) : Bool", "Returns true if self is less than other."], - ["<", "<(other : Int16) : Bool", "Returns true if self is less than other."], - ["<", "<(other : Int8) : Bool", "Returns true if self is less than other."], - ["<", "<(other : UInt8) : Bool", "Returns true if self is less than other."], - ["<", "<(other : Int64) : Bool", "Returns true if self is less than other."], - ["<", "<(other : UInt32) : Bool", "Returns true if self is less than other."], - ["<", "<(other : UInt64) : Bool", "Returns true if self is less than other."], - ["<", "<(other : Float32) : Bool", "Returns true if self is less than other."], - ["<", "<(other : Float64) : Bool", "Returns true if self is less than other."], - ["<=", "<=(other : Int8) : Bool", "Returns true if self is less than or equal to other."], - ["<=", "<=(other : Float64) : Bool", "Returns true if self is less than or equal to other."], - ["<=", "<=(other : Float32) : Bool", "Returns true if self is less than or equal to other."], - ["<=", "<=(other : UInt64) : Bool", "Returns true if self is less than or equal to other."], - ["<=", "<=(other : UInt32) : Bool", "Returns true if self is less than or equal to other."], - ["<=", "<=(other : UInt16) : Bool", "Returns true if self is less than or equal to other."], - ["<=", "<=(other : Int16) : Bool", "Returns true if self is less than or equal to other."], - ["<=", "<=(other : UInt8) : Bool", "Returns true if self is less than or equal to other."], - ["<=", "<=(other : Int32) : Bool", "Returns true if self is less than or equal to other."], - ["<=", "<=(other : Int64) : Bool", "Returns true if self is less than or equal to other."], - ["==", "==(other : Float64) : Bool", "Returns true if self is equal to other."], - ["==", "==(other : Float32) : Bool", "Returns true if self is equal to other."], - ["==", "==(other : UInt64) : Bool", "Returns true if self is equal to other."], - ["==", "==(other : UInt32) : Bool", "Returns true if self is equal to other."], - ["==", "==(other : UInt16) : Bool", "Returns true if self is equal to other."], - ["==", "==(other : UInt8) : Bool", "Returns true if self is equal to other."], - ["==", "==(other : Int64) : Bool", "Returns true if self is equal to other."], - ["==", "==(other : Int32) : Bool", "Returns true if self is equal to other."], - ["==", "==(other : Int8) : Bool", "Returns true if self is equal to other."], - ["==", "==(other : Int16) : Bool", "Returns true if self is equal to other."], - [">", ">(other : Int8) : Bool", "Returns true if self is greater than other."], - [">", ">(other : Int16) : Bool", "Returns true if self is greater than other."], - [">", ">(other : Int32) : Bool", "Returns true if self is greater than other."], - [">", ">(other : Int64) : Bool", "Returns true if self is greater than other."], - [">", ">(other : UInt8) : Bool", "Returns true if self is greater than other."], - [">", ">(other : UInt16) : Bool", "Returns true if self is greater than other."], - [">", ">(other : UInt32) : Bool", "Returns true if self is greater than other."], - [">", ">(other : UInt64) : Bool", "Returns true if self is greater than other."], - [">", ">(other : Float32) : Bool", "Returns true if self is greater than other."], - [">", ">(other : Float64) : Bool", "Returns true if self is greater than other."], - [">=", ">=(other : UInt16) : Bool", "Returns true if self is greater than or equal to other."], - [">=", ">=(other : Int8) : Bool", "Returns true if self is greater than or equal to other."], - [">=", ">=(other : Int16) : Bool", "Returns true if self is greater than or equal to other."], - [">=", ">=(other : Int32) : Bool", "Returns true if self is greater than or equal to other."], - [">=", ">=(other : Int64) : Bool", "Returns true if self is greater than or equal to other."], - [">=", ">=(other : UInt8) : Bool", "Returns true if self is greater than or equal to other."], - [">=", ">=(other : Float64) : Bool", "Returns true if self is greater than or equal to other."], - [">=", ">=(other : Float32) : Bool", "Returns true if self is greater than or equal to other."], - [">=", ">=(other : UInt64) : Bool", "Returns true if self is greater than or equal to other."], - [">=", ">=(other : UInt32) : Bool", "Returns true if self is greater than or equal to other."], - ["^", "^(other : Int8) : self", "Returns the result of performing a bitwise XOR of self's and other's bits."], - ["^", "^(other : Int32) : self", "Returns the result of performing a bitwise XOR of self's and other's bits."], - ["^", "^(other : Int64) : self", "Returns the result of performing a bitwise XOR of self's and other's bits."], - ["^", "^(other : UInt8) : self", "Returns the result of performing a bitwise XOR of self's and other's bits."], - ["^", "^(other : UInt16) : self", "Returns the result of performing a bitwise XOR of self's and other's bits."], - ["^", "^(other : UInt32) : self", "Returns the result of performing a bitwise XOR of self's and other's bits."], - ["^", "^(other : UInt64) : self", "Returns the result of performing a bitwise XOR of self's and other's bits."], - ["^", "^(other : Int16) : self", "Returns the result of performing a bitwise XOR of self's and other's bits."], - ["to_f", "to_f : Float64", "Returns self converted to Float64."], - ["to_f32", "to_f32 : Float32", "Returns self converted to Float32."], - ["to_f64", "to_f64 : Float64", "Returns self converted to Float64."], - ["to_i", "to_i : Int32", "Returns self converted to Int32."], - ["to_i16", "to_i16 : Int16", "Returns self converted to Int16."], - ["to_i32", "to_i32 : Int32", "Returns self converted to Int32."], - ["to_i64", "to_i64 : Int64", "Returns self converted to Int64."], - ["to_i8", "to_i8 : Int8", "Returns self converted to Int8."], - ["to_u", "to_u : UInt32", "Returns self converted to UInt32."], - ["to_u16", "to_u16 : UInt16", "Returns self converted to UInt16."], - ["to_u32", "to_u32 : UInt32", "Returns self converted to UInt32."], - ["to_u64", "to_u64 : UInt64", "Returns self converted to UInt64."], - ["to_u8", "to_u8 : UInt8", "Returns self converted to UInt8."], - ["unsafe_chr", "unsafe_chr : Char", "Returns a Char that has the unicode codepoint of self, without checking if this integer is in the range valid for chars (0..0x10ffff)."], - ["|", "|(other : UInt64) : self", "Returns the result of performing a bitwise OR of self's and other's bits."], - ["|", "|(other : UInt32) : self", "Returns the result of performing a bitwise OR of self's and other's bits."], - ["|", "|(other : UInt16) : self", "Returns the result of performing a bitwise OR of self's and other's bits."], - ["|", "|(other : UInt8) : self", "Returns the result of performing a bitwise OR of self's and other's bits."], - ["|", "|(other : Int64) : self", "Returns the result of performing a bitwise OR of self's and other's bits."], - ["|", "|(other : Int32) : self", "Returns the result of performing a bitwise OR of self's and other's bits."], - ["|", "|(other : Int16) : self", "Returns the result of performing a bitwise OR of self's and other's bits."], - ["|", "|(other : Int8) : self", "Returns the result of performing a bitwise OR of self's and other's bits.", 5] -] -export const FLOAT_METHODS = [ - ["%", "%(other)", ""], - ["-", "-", ""], - ["<=>", "<=>(other : BigRational)", ""], - ["<=>", "<=>(other : BigInt)", ""], - ["days", "days", ""], - ["fdiv", "fdiv(other)", ""], - ["finite?", "finite?", ""], - ["hours", "hours", ""], - ["infinite?", "infinite?", ""], - ["milliseconds", "milliseconds", ""], - ["minutes", "minutes", ""], - ["modulo", "modulo(other)", ""], - ["nan?", "nan?", ""], - ["remainder", "remainder(other)", ""], - ["seconds", "seconds", ""], - ["to_json", "to_json(json : JSON::Builder)", ""] -] -export const CHAR_METHODS = [ - ["%", "%(other)", ""], - ["-", "-", ""], - ["<=>", "<=>(other : BigRational)", ""], - ["<=>", "<=>(other : BigInt)", ""], - ["days", "days", ""], - ["fdiv", "fdiv(other)", ""], - ["finite?", "finite?", ""], - ["hours", "hours", ""], - ["infinite?", "infinite?", ""], - ["milliseconds", "milliseconds", ""], - ["minutes", "minutes", ""], - ["modulo", "modulo(other)", ""], - ["nan?", "nan?", ""], - ["remainder", "remainder(other)", ""], - ["seconds", "seconds", ""], - ["to_json", "to_json(json : JSON::Builder)", ""], - ["to_big_i", "to_big_i : BigInt", "Returns a BigInt representing this float (rounded using floor)."], - ["to_io", "to_io(io : IO, format : IO::ByteFormat)", "Writes this float to the given io in the given format."] -] -export const STRING_METHODS = [ - ["build", "build(capacity = 64, &block) : self", "Builds a String by creating a String::Builder with the given initial capacity, yielding it to the block and finally getting a String out of it."], - ["%", "%(other)", "Interpolates other into the string using Kernel#sprintf."], - ["*", "*(times : Int)", "Makes a new String by adding str to itself times times."], - ["+", "+(other : self)", "Concatenates str and other."], - ["+", "+(char : Char)", "Concatenates str and other."], - ["<=>", "<=>(other : self)", "1, 0 or +1 depending on whether this string is less, equal or greater than other."], - ["=~", "=~(other)", "Tests whether str matches regex."], - ["=~", "=~(regex : Regex)", "Tests whether str matches regex."], - ["[]", "[](start : Int, count : Int)", "Returns a substring starting from the start character of size count."], - ["[]", "[](index : Int)", "Returns the Char at the given index, or raises IndexError if out of bounds."], - ["[]", "[](range : Range(Int, Int))", "Returns a substring by using a Range's begin and end as character indices."], - ["ascii_only?", "ascii_only?", "Returns true if this String is comprised in its entirety by ASCII characters."], - ["blank?", "blank?", "Returns true if this string consists exclusively of unicode whitespace."], - ["byte_index_to_char_index", "byte_index_to_char_index(index)", "Returns the char index of a byte index, or nil if out of bounds."], - ["bytes", "bytes", "Returns this string's bytes as an Array(UInt8)."], - ["bytesize", "bytesize : Int32", "Returns the number of bytes in this string."], - ["camelcase", "camelcase", "Converts underscores to camelcase boundaries."], - ["capitalize", "capitalize(options = Unicode::CaseOptions::None)", "Returns a new String with the first letter converted to uppercase and every subsequent letter converted to lowercase."], - ["char_index_to_byte_index", "char_index_to_byte_index(index)", "Returns the byte index of a char index, or nil if out of bounds."], - ["chars", "chars", "Returns an Array of all characters in the string."], - ["check_no_null_byte", "check_no_null_byte", "Raises an ArgumentError if self has null bytes."], - ["chomp", "chomp", "Returns a new String with the last carriage return removed (that is, it will remove \n, \r, and \r\n)."], - ["chomp", "chomp(suffix : String)", "Returns a new String with suffix removed from the end of the string."], - ["chomp", "chomp(suffix : Char)", "Returns a new String with suffix removed from the end of the string."], - ["codepoints", "codepoints", "Returns an Array of the codepoints that make the string."], - ["compare", "compare(other : String, case_insensitive = false)", "1, 0 or +1 depending on whether this string is less, equal or greater than other, optionally in a case_insensitive manner."], - ["count", "count(&block)", "Yields each char in this string to the block, returns the number of times the block returned a truthy value."], - ["count", "count(other : Char)", "Counts the occurrences of other char in this string."], - ["count", "count(*sets)", "Sets should be a list of strings following the rules described at Char#in_set?."], - ["delete", "delete(&block)", "Yields each char in this string to the block."], - ["delete", "delete(char : Char)", "Returns a new String with all occurrences of char removed."], - ["delete", "delete(*sets)", "Sets should be a list of strings following the rules described at Char#in_set?."], - ["downcase", "downcase(options = Unicode::CaseOptions::None)", "Returns a new String with each uppercase letter replaced with its lowercase counterpart."], - ["each_byte", "each_byte", "Returns an Iterator over each byte in the string."], - ["each_byte", "each_byte(&block)", "Yields each byte in the string to the block."], - ["each_char", "each_char", "Returns an Iterator over each character in the string."], - ["each_char", "each_char(&block) : Nil", "Yields each character in the string to the block."], - ["each_char_with_index", "each_char_with_index(&block)", "Yields each character and its index in the string to the block."], - ["each_codepoint", "each_codepoint(&block)", "Yields each codepoint to the block."], - ["each_codepoint", "each_codepoint", "Returns an Iterator for each codepoint."], - ["each_line", "each_line(chomp = true)", "Returns an Iterator which yields each line of this string (see String#each_line)."], - ["each_line", "each_line(chomp = true, &block) : Nil", "Splits the string after each newline and yields each line to a block."], - ["empty?", "empty?", "Returns true if this is the empty string, \"\"."], - ["encode", "encode(encoding : String, invalid : Symbol? = nil) : Bytes", "Returns a slice of bytes containing this string encoded in the given encoding."], - ["gsub", "gsub(string : String, &block)", "Returns a String where all occurrences of the given string are replaced with the block's value."], - ["gsub", "gsub(&block : Char -> _)", "Returns a String where each character yielded to the given block is replaced by the block's return value."], - ["gsub", "gsub(char : Char, replacement)", "Returns a String where all occurrences of the given char are replaced with the given replacement."], - ["gsub", "gsub(pattern : Regex, hash : Hash(String, _) | NamedTuple)", "Returns a String where all occurrences of the given pattern are replaced with a hash of replacements."], - ["gsub", "gsub(pattern : Regex, replacement, backreferences = true)", "Returns a String where all occurrences of the given pattern are replaced with the given replacement."], - ["gsub", "gsub(string : String, replacement)", "Returns a String where all occurrences of the given string are replaced with the given replacement."], - ["gsub", "gsub(hash : Hash(Char, _))", "Returns a String where all chars in the given hash are replaced by the corresponding hash values."], - ["gsub", "gsub(tuple : NamedTuple)", "Returns a String where all chars in the given named tuple are replaced by the corresponding tuple values."], - ["gsub", "gsub(pattern : Regex, &block)", "Returns a String where all occurrences of the given pattern are replaced by the block value's value."], - ["has_back_references?", "has_back_references?", "This returns true if this string has '\\' in it."], - ["hash", "hash", "Returns a hash based on this string’s size and content."], - ["hexbytes", "hexbytes : Bytes", "Interprets this string as containing a sequence of hexadecimal values and decodes it as a slice of bytes."], - ["hexbytes??", "hexbytes? : Bytes?", "Interprets this string as containing a sequence of hexadecimal values and decodes it as a slice of bytes."], - ["includes?", "includes?(search : Char | String)", "Returns true if the string contains search."], - ["index", "index(search : Regex, offset = 0)", "Returns the index of search in the string, or nil if the string is not present."], - ["index", "index(search : String, offset = 0)", "Returns the index of search in the string, or nil if the string is not present."], - ["index", "index(search : Char, offset = 0)", "Returns the index of search in the string, or nil if the string is not present."], - ["insert", "insert(index : Int, other : Char)", "Returns a new String that results of inserting other in self at index."], - ["insert", "insert(index : Int, other : String)", "Returns a new String that results of inserting other in self at index."], - ["lchop", "lchop", "Returns a new String with the first char removed from it."], - ["lchop", "lchop(prefix : String)", "Returns a new String with prefix removed from the beginning of the string."], - ["lchop", "lchop(prefix : Char)", "Returns a new String with prefix removed from the beginning of the string."], - ["ljust", "ljust(len, char : Char = ' ')", "Adds instances of char to right of the string until it is at least size of len."], - ["lstrip", "lstrip(char : Char)", "Returns a new string with leading occurrences of char removed."], - ["lstrip", "lstrip", "Returns a new String with leading whitespace removed."], - ["lstrip", "lstrip(&block : Char -> _)", "Returns a new string where leading characters for which the block returns a truthy value are removed."], - ["lstrip", "lstrip(chars : String)", "Returns a new string where leading occurrences of any char in chars are removed."], - ["match", "match(regex : Regex, pos = 0) : Regex::MatchData?", "Finds match of regex, starting at pos."], - ["partition", "partition(search : Regex) : Tuple(String, String, String)", "Searches separator or pattern (Regex) in the string, and returns a Tuple with the part before it, the match, and the part after it."], - ["partition", "partition(search : Char | String) : Tuple(String, String, String)", "Searches separator or pattern (Regex) in the string, and returns a Tuple with the part before it, the match, and the part after it."], - ["rchop", "rchop", "Returns a new String with the last character removed."], - ["rchop", "rchop(suffix : String)", "Returns a new String with suffix removed from the end of the string."], - ["rchop", "rchop(suffix : Char)", "Returns a new String with suffix removed from the end of the string."], - ["reverse", "reverse", "Reverses the order of characters in the string."], - ["rindex", "rindex(search : Char, offset = size - 1)", "Returns the index of the last appearance of search in the string, If offset is present, it defines the position to end the search (characters beyond this point are ignored)."], - ["rindex", "rindex(search : String, offset = size - search.size)", "Returns the index of the last appearance of search in the string, If offset is present, it defines the position to end the search (characters beyond this point are ignored)."], - ["rindex", "rindex(search : Regex, offset = 0)", "Returns the index of the last appearance of search in the string, If offset is present, it defines the position to end the search (characters beyond this point are ignored)."], - ["rjust", "rjust(len, char : Char = ' ')", "Adds instances of char to left of the string until it is at least size of len."], - ["rpartition", "rpartition(search : Char | String) : Tuple(String, String, String)", "Searches separator or pattern (Regex) in the string from the end of the string, and returns a Tuple with the part before it, the match, and the part after it."], - ["rpartition", "rpartition(search : Regex) : Tuple(String, String, String)", "Searches separator or pattern (Regex) in the string from the end of the string, and returns a Tuple with the part before it, the match, and the part after it."], - ["rstrip", "rstrip", "Returns a new String with trailing whitespace removed."], - ["rstrip", "rstrip(chars : String)", "Returns a new string where trailing occurrences of any char in chars are removed."], - ["rstrip", "rstrip(char : Char)", "Returns a new string with trailing occurrences of char removed."], - ["rstrip", "rstrip(&block : Char -> _)", "Returns a new string where trailing characters for which the block returns a truthy value are removed."], - ["scan", "scan(pattern : String)", "Searches the string for instances of pattern, returning an array of the matched string for each match."], - ["scan", "scan(pattern : Regex, &block)", "Searches the string for instances of pattern, yielding a Regex::MatchData for each match."], - ["scan", "scan(pattern : Regex)", "Searches the string for instances of pattern, returning an Array of Regex::MatchData for each match."], - ["scan", "scan(pattern : String, &block)", "Searches the string for instances of pattern, yielding the matched string for each match."], - ["scrub", "scrub(replacement = Char::REPLACEMENT) : String", "8 encoding are replaced with replacement."], - ["size", "size", "Returns the number of unicode codepoints in this string."], - ["split", "split(separator : Regex, limit = nil, &block : String -> _)", "Makes an Array by splitting the string on separator (and removing instances of separator)."], - ["split", "split(separator : Regex, limit = nil)", "Splits the string after each regex separator and yields each part to a block."], - ["split", "split(separator : String, limit = nil, &block : String -> _)", "Splits the string after each string separator and yields each part to a block."], - ["split", "split(limit : Int32? = nil)", "Makes an array by splitting the string on any ASCII whitespace characters (and removing that whitespace)."], - ["split", "split(limit : Int32? = nil, &block : String -> _)", "Splits the string after any ASCII whitespace character and yields each part to a block."], - ["split", "split(separator : Char, limit = nil)", "Makes an Array by splitting the string on the given character separator (and removing that character)."], - ["split", "split(separator : String, limit = nil)", "Makes an Array by splitting the string on separator (and removing instances of separator)."], - ["split", "split(separator : Char, limit = nil, &block : String -> _)", "Splits the string after each character separator and yields each part to a block."], - ["squeeze", "squeeze(&block)", "Yields each char in this string to the block."], - ["squeeze", "squeeze(char : Char)", "Returns a new String, with all runs of char replaced by one instance."], - ["squeeze", "squeeze", "Returns a new String, that has all characters removed, that were the same as the previous one."], - ["squeeze", "squeeze(*sets : String)", "Sets should be a list of strings following the rules described at Char#in_set?."], - ["strip", "strip(&block : Char -> _)", "Returns a new string where leading and trailing characters for which the block returns a truthy value are removed."], - ["strip", "strip", "Returns a new String with leading and trailing whitespace removed."], - ["strip", "strip(chars : String)", "Returns a new string where leading and trailing occurrences of any char in chars are removed."], - ["strip", "strip(char : Char)", "Returns a new string where leading and trailing occurrences of char are removed."], - ["sub", "sub(pattern : Regex, &block)", "Returns a String where the first occurrence of pattern is replaced by the block's return value."], - ["sub", "sub(index : Int, replacement : Char)", "Returns a new String with the character at the given index replaced by replacement."], - ["sub", "sub(index : Int, replacement : String)", "Returns a new String with the character at the given index replaced by replacement."], - ["sub", "sub(range : Range(Int, Int), replacement : Char)", "Returns a new String with characters at the given range replaced by replacement."], - ["sub", "sub(range : Range(Int, Int), replacement : String)", "Returns a new String with characters at the given range replaced by replacement."], - ["sub", "sub(hash : Hash(Char, _))", "Returns a String where the first char in the string matching a key in the given hash is replaced by the corresponding hash value."], - ["sub", "sub(&block : Char -> _)", "Returns a new String where the first character is yielded to the given block and replaced by its return value."], - ["sub", "sub(char : Char, replacement)", "Returns a String where the first occurrence of char is replaced by replacement."], - ["sub", "sub(string : String, &block)", "Returns a String where the first occurrences of the given string is replaced with the block's value."], - ["sub", "sub(pattern : Regex, replacement, backreferences = true)", "Returns a String where the first occurrence of pattern is replaced by replacement"], - ["sub", "sub(pattern : Regex, hash : Hash(String, _) | NamedTuple)", "Returns a String where the first occurrences of the given pattern is replaced with the matching entry from the hash of replacements."], - ["sub", "sub(string : String, replacement)", "Returns a String where the first occurrences of the given string is replaced with the given replacement."], - ["succ", "succ", "Returns the successor of the string."], - ["to_big_i", "to_big_i(base = 10) : BigInt", "Returns a BigInt from this string, in the given base."], - ["to_f", "to_f(whitespace = true, strict = true)", "Returns the result of interpreting characters in this string as a floating point number (Float64)."], - ["to_f32", "to_f32(whitespace = true, strict = true)", "Same as #to_f but returns a Float32."], - ["to_f32?", "to_f32?(whitespace = true, strict = true)", "Same as #to_f? but returns a Float32."], - ["to_f64", "to_f64(whitespace = true, strict = true)", "Same as #to_f."], - ["to_f64?", "to_f64?(whitespace = true, strict = true)", "Same as #to_f?."], - ["to_f?", "to_f?(whitespace = true, strict = true)", "Returns the result of interpreting characters in this string as a floating point number (Float64)."], - ["to_i", "to_i(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block)", "Same as #to_i, but returns the block's value if there is not a valid number at the start of this string, or if the resulting integer doesn't fit an Int32."], - ["to_i", "to_i(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true)", "Returns the result of interpreting leading characters in this string as an integer base base (between 2 and 36)."], - ["to_i16", "to_i16(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block)", "Same as #to_i but returns an Int16 or the block's value."], - ["to_i16", "to_i16(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int16", "Same as #to_i but returns an Int16."], - ["to_i16?", "to_i16?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int16?", "Same as #to_i but returns an Int16 or nil."], - ["to_i32", "to_i32(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int32", "Same as #to_i."], - ["to_i32", "to_i32(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block)", "Same as #to_i."], - ["to_i32?", "to_i32?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int32?", "Same as #to_i."], - ["to_i64", "to_i64(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block)", "Same as #to_i but returns an Int64 or the block's value."], - ["to_i64", "to_i64(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int64", "Same as #to_i but returns an Int64."], - ["to_i64?", "to_i64?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int64?", "Same as #to_i but returns an Int64 or nil."], - ["to_i8", "to_i8(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int8", "Same as #to_i but returns an Int8."], - ["to_i8", "to_i8(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block)", "Same as #to_i but returns an Int8 or the block's value."], - ["to_i8?", "to_i8?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int8?", "Same as #to_i but returns an Int8 or nil."], - ["to_i?", "to_i?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true)", "Same as #to_i, but returns nil if there is not a valid number at the start of this string, or if the resulting integer doesn't fit an Int32."], - ["to_slice", "to_slice : Bytes", "Returns the underlying bytes of this String in an unsafe way."], - ["to_u16", "to_u16(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt16", "Same as #to_i but returns an UInt16."], - ["to_u16", "to_u16(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block)", "Same as #to_i but returns an UInt16 or the block's value."], - ["to_u16?", "to_u16?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt16?", "Same as #to_i but returns an UInt16 or nil."], - ["to_u32", "to_u32(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block)", "Same as #to_i but returns an UInt32 or the block's value."], - ["to_u32", "to_u32(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt32", "Same as #to_i but returns an UInt32."], - ["to_u32?", "to_u32?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt32?", "Same as #to_i but returns an UInt32 or nil."], - ["to_u64", "to_u64(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block)", "Same as #to_i but returns an UInt64 or the block's value."], - ["to_u64", "to_u64(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt64", "Same as #to_i but returns an UInt64."], - ["to_u64?", "to_u64?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt64?", "Same as #to_i but returns an UInt64 or nil."], - ["to_u8", "to_u8(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt8", "Same as #to_i but returns an UInt8."], - ["to_u8", "to_u8(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block)", "Same as #to_i but returns an UInt8 or the block's value."], - ["to_u8?", "to_u8?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt8?", "Same as #to_i but returns an UInt8 or nil."], - ["to_unsafe", "to_unsafe : Pointer(UInt8)", "Returns a pointer to the underlying bytes of this String."], - ["tr", "tr(from : String, to : String)", "Returns a new string translating characters using from and to as a map."], - ["underscore", "underscore", "Converts camelcase boundaries to underscores."], - ["upcase", "upcase(options = Unicode::CaseOptions::None)", "Returns a new String with each lowercase letter replaced with its uppercase counterpart."], - ["valid_encoding?", "valid_encoding?", "8 encoding."], - ["==", "==(other : self)", ""], - ["[]", "[](regex : Regex, group)", ""], - ["[]", "[](str : String | Char)", ""], - ["[]", "[](regex : Regex)", ""], - ["[]?", "[]?(regex : Regex)", ""], - ["[]?", "[]?(index : Int)", ""], - ["[]?", "[]?(regex : Regex, group)", ""], - ["[]?", "[]?(str : String | Char)", ""], - ["at", "at(index : Int)", ""], - ["at", "at(index : Int, &block)", ""], - ["byte_at", "byte_at(index, &block)", ""], - ["byte_at", "byte_at(index)", ""], - ["byte_at?", "byte_at?(index)", ""], - ["byte_index", "byte_index(string : String, offset = 0)", ""], - ["byte_index", "byte_index(byte : Int, offset = 0)", ""], - ["byte_slice", "byte_slice(start : Int, count : Int)", ""], - ["byte_slice", "byte_slice(start : Int)", ""], - ["char_at", "char_at(index)", ""], - ["clone", "clone", ""], - ["codepoint_at", "codepoint_at(index)", ""], - ["dump", "dump(io)", ""], - ["dump", "dump", ""], - ["dump_unquoted", "dump_unquoted(io)", ""], - ["dump_unquoted", "dump_unquoted", ""], - ["dup", "dup", ""], - ["ends_with?", "ends_with?(char : Char)", ""], - ["ends_with?", "ends_with?(str : String)", ""], - ["inspect", "inspect(io)", ""], - ["inspect_unquoted", "inspect_unquoted", ""], - ["inspect_unquoted", "inspect_unquoted(io)", ""], - ["lines", "lines(chomp = true)", ""], - ["pretty_print", "pretty_print(pp)", ""], - ["starts_with?", "starts_with?(char : Char)", ""], - ["starts_with?", "starts_with?(str : String)", ""], - ["to_big_f", "to_big_f", ""], - ["to_json", "to_json(json : JSON::Builder)", ""], - ["to_s", "to_s(io)", ""], - ["to_s", "to_s", ""], - ["to_yaml", "to_yaml(yaml : YAML::Builder)", ""], - ["unsafe_byte_at", "unsafe_byte_at(index)", ""], - ["unsafe_byte_slice", "unsafe_byte_slice(byte_offset, count)", ""], - ["unsafe_byte_slice", "unsafe_byte_slice(byte_offset)", ""] -] -export const SYMBOLS_METHODS = [ - ["!=", "!=(other : Symbol) : Bool", "Returns true if self is not equal to other."], - ["<=>", "<=>(other : Symbol)", "Compares symbol with other based on String#<=> method."], - ["==", "==(other : Symbol) : Bool", "Returns true if self is equal to other."], - ["hash", "hash : Int32", "Generates an Int32 hash value for this symbol."], - ["inspect", "inspect(io : IO)", "Returns the symbol literal representation as a string."], - ["to_i", "to_i : Int32", "Returns a unique number for this symbol."], - ["to_s", "to_s(io : IO)", "Appends the symbol's name to the passed IO."], - ["to_s", "to_s : String", "Returns the symbol's name as a String."], - ["to_yaml", "to_yaml(yaml : YAML::Builder)", ""], - ["to_json", "to_json(json : JSON::Builder)", ""], - ["clone", "clone", ""] -] -export const ARRAY_METHODS = [ - ["includes?", "includes?(other : self)", "Returns true if the array contains search."], - ["join", "join(separator, io)", "Prints to io all the elements in the collection, separated by separator.", 5], - ["join", "join(separator = \"\")", "Returns a String created by concatenating the elements in the collection, separated by separator (defaults to none).", 5], - ["join", "join(separator, io, &block)", "Prints to io the concatenation of the elements, with the possibility of controlling how the printing is done via a block.", 5], - ["join", "join(separator = \"\", &block)", "Returns a String created by concatenating the results of passing the elements in the collection to the passed block, separated by separator (defaults to none).", 5], - ["each", "each", "Returns an iterator over the array entries.", 5], - ["each", "each(&block) : Nil", "Calls the given block for each element and passes in the element.", 5], - ["build", "build(capacity : Int, &block)", "Creates a new Array, allocating an internal buffer with the given capacity, and yielding that buffer."], - ["each_product", "each_product(arrays : Array(Array), reuse = false, &block)", ""], - ["each_product", "each_product(*arrays : Array, reuse = false, &block)", ""], - ["from_json", "from_json(string_or_io, &block) : Nil", "Parses a String or IO denoting a JSON array, yielding each of its elements to the given block."], - ["from_yaml", "from_yaml(string_or_io, &block)", ""], - ["product", "product(arrays)", ""], - ["product", "product(*arrays : Array)", ""], - ["&", "&(other : Array(U)) forall U", "Set intersection: returns a new Array containing elements common to self and other, excluding any duplicates."], - ["*", "*(times : Int)", "Repetition: Returns a new Array built by concatenating times copies of self."], - ["+", "+(other : Array(U)) forall U", "Concatenation."], - ["-", "-(other : Array(U)) forall U", "Difference."], - ["<<", "<<(value : T)", "Append."], - ["<=>", "<=>(other : Array)", "Combined comparison operator."], - ["==", "==(other : Array)", "Equality."], - ["[]", "[](range : Range(Int, Int))", "Returns all elements that are within the given range."], - ["[]", "[](start : Int, count : Int)", "Returns count or less (if there aren't enough) elements starting at the given start index."], - ["[]=", "[]=(range : Range(Int, Int), values : Array(T))", "Replaces a subrange with the elements of the given array."], - ["[]=", "[]=(range : Range(Int, Int), value : T)", "Replaces a subrange with a single value."], - ["[]=", "[]=(index : Int, value : T)", "Sets the given value at the given index."], - ["[]=", "[]=(index : Int, count : Int, values : Array(T))", "Replaces a subrange with the elements of the given array."], - ["[]=", "[]=(index : Int, count : Int, value : T)", "Replaces a subrange with a single value."], - ["clear", "clear", "Removes all elements from self."], - ["clone", "clone", "Returns a new Array that has self's elements cloned."], - ["compact", "compact", "Returns a copy of self with all nil elements removed."], - ["compact!", "compact!", "Removes all nil elements from self."], - ["concat", "concat(other : Enumerable)", "Appends the elements of other to self, and returns self."], - ["concat", "concat(other : Array)", "Appends the elements of other to self, and returns self."], - ["delete", "delete(obj)", "Removes all items from self that are equal to obj."], - ["delete_at", "delete_at(index : Int, count : Int)", "Removes count elements from self starting at index."], - ["delete_at", "delete_at(index : Int)", "Removes the element at index, returning that element."], - ["delete_at", "delete_at(range : Range(Int, Int))", "Removes all elements within the given range."], - ["dup", "dup", "Returns a new Array that has exactly self's elements."], - ["each_permutation", "each_permutation(size : Int = self.size, reuse = false)", "Returns an Iterator over each possible permutation of size of self."], - ["each_permutation", "each_permutation(size : Int = self.size, reuse = false, &block) : Nil", "Yields each possible permutation of size of self."], - ["fill", "fill(from : Int, count : Int, &block)", "Yields each index of self, starting at from and just count times, to the given block and then assigns the block's value in that position."], - ["fill", "fill(from : Int, &block)", "Yields each index of self, starting at from, to the given block and then assigns the block's value in that position."], - ["fill", "fill(range : Range(Int, Int), &block)", "Yields each index of self, in the given range, to the given block and then assigns the block's value in that position."], - ["fill", "fill(&block)", "Yields each index of self to the given block and then assigns the block's value in that position."], - ["fill", "fill(value : T, from : Int, count : Int)", "Replaces every element in self, starting at from and only count times, with the given value."], - ["fill", "fill(value : T, from : Int)", "Replaces every element in self, starting at from, with the given value."], - ["fill", "fill(value : T, range : Range(Int, Int))", "Replaces every element in range with value."], - ["fill", "fill(value : T)", "Replaces every element in self with the given value."], - ["first", "first(n : Int)", "Returns the first n elements of the array."], - ["flatten", "flatten", "Returns a new Array that is a one-dimensional flattening of self (recursively)."], - ["insert", "insert(index : Int, object : T)", "Insert object before the element at index and shifting successive elements, if any."], - ["last", "last(n : Int)", "Returns the last n elements of the array."], - ["map", "map(&block : T -> U) forall U", "Optimized version of Enumerable#map."], - ["map!", "map!(&block)", "Invokes the given block for each element of self, replacing the element with the value returned by the block."], - ["map_with_index", "map_with_index(&block : T, Int32 -> U) forall U", "Optimized version of Enumerable#map_with_index."], - ["permutations", "permutations(size : Int = self.size)", "Returns an Array with all possible permutations of size."], - ["pop", "pop(&block)", "Removes the last value from self."], - ["pop", "pop", "Removes the last value from self, at index size - 1."], - ["pop", "pop(n : Int)", "Removes the last n values from self, at index size - 1."], - ["pop?", "pop?", "Like #pop, but returns nil if self is empty."], - ["push", "push(*values : T)", "Append multiple values."], - ["push", "push(value : T)", "Append."], - ["reject!", "reject!(&block)", "Modifies self, deleting the elements in the collection for which the passed block returns true."], - ["reverse", "reverse", "Returns an array with all the elements in the collection reversed."], - ["reverse!", "reverse!", "Reverses in-place all the elements of self."], - ["sample", "sample(n : Int, random = Random::DEFAULT)", "Returns n number of random elements from self, using the given random number generator."], - ["select!", "select!(&block)", "Modifies self, keeping only the elements in the collection for which the passed block returns true."], - ["shift", "shift(n : Int)", "Removes the first n values of self, starting at index 0."], - ["shift", "shift", "Removes the first value of self, at index 0."], - ["shuffle", "shuffle(random = Random::DEFAULT)", "Returns an array with all the elements in the collection randomized using the given random number generator."], - ["shuffle!", "shuffle!(random = Random::DEFAULT)", "Modifies self by randomizing the order of elements in the collection using the given random number generator."], - ["size", "size : Int32", "Returns the number of elements in the array."], - ["sort", "sort", "Returns an array with all elements in the collection sorted."], - ["sort!", "sort!", "Modifies self by sorting the elements in the collection."], - ["to_unsafe", "to_unsafe : Pointer(T)", "Returns a pointer to the internal buffer where self's elements are stored."], - ["transpose", "transpose", "Assumes that self is an array of arrays and transposes the rows and columns."], - ["uniq", "uniq", "Returns a new Array by removing duplicate values in self."], - ["uniq", "uniq(&block : T -> _)", "Returns a new Array by removing duplicate values in self, using the block's value for comparison."], - ["uniq!", "uniq!", "Removes duplicate elements from self."], - ["uniq!", "uniq!(&block)", "Removes duplicate elements from self, using the block's value for comparison."], - ["unshift", "unshift(obj : T)", "Prepend."], - ["unshift", "unshift(*values : T)", "Prepend multiple values."], - ["|", "|(other : Array(U)) forall U", "Set union: returns a new Array by joining self with other, excluding any duplicates, and preserving the order from self."], - ["combinations", "combinations(size : Int = self.size)", ""], - ["each_combination", "each_combination(size : Int = self.size, reuse = false)", ""], - ["each_combination", "each_combination(size : Int = self.size, reuse = false, &block) : Nil", ""], - ["each_repeated_combination", "each_repeated_combination(size : Int = self.size, reuse = false, &block) : Nil", ""], - ["each_repeated_combination", "each_repeated_combination(size : Int = self.size, reuse = false)", ""], - ["each_repeated_permutation", "each_repeated_permutation(size : Int = self.size, reuse = false, &block) : Nil", ""], - ["pretty_print", "pretty_print(pp) : Nil", ""], - ["product", "product(enumerable : Enumerable, &block)", ""], - ["product", "product(ary : Array(U)) forall U", ""], - ["repeated_combinations", "repeated_combinations(size : Int = self.size)", ""], - ["repeated_permutations", "repeated_permutations(size : Int = self.size)", ""], - ["replace", "replace(other : Array)", ""], - ["rotate", "rotate(n = 1)", ""], - ["rotate!", "rotate!(n = 1)", ""], - ["shift", "shift(&block)", ""], - ["shift?", "shift?", ""], - ["sort", "sort(&block : T, T -> Int32)", ""], - ["sort!", "sort!(&block : T, T -> Int32)", ""], - ["sort_by", "sort_by(&block : T -> _)", ""], - ["sort_by!", "sort_by!(&block : T -> _)", ""], - ["swap", "swap(index0, index1)", ""], - ["to_a", "to_a", ""], - ["to_json", "to_json(json : JSON::Builder)", ""], - ["to_s", "to_s(io : IO)", ""], - ["to_yaml", "to_yaml(yaml : YAML::Builder)", ""], - ["unsafe_at", "unsafe_at(index : Int)", ""], - ["update", "update(index : Int, &block)", ""] -] -export const HASH_METHODS = [ - ["zip", "zip(ary1 : Array(K), ary2 : Array(V))", "Zips two arrays into a Hash, taking keys from ary1 and values from ary2.", 5], - ["==", "==(other : Hash)", "Compares with other.", 5], - ["[]", "[](key)", "See also: Hash#fetch.", 5], - ["[]=", "[]=(key : K, value : V)", "Sets the value of key to the given value.", 5], - ["[]?", "[]?(key)", "Returns the value for the key given by key.", 5], - ["clear", "clear", "Empties a Hash and returns it.", 5], - ["clone", "clone", "Similar to #dup, but duplicates the values as well.", 5], - ["compact", "compact", "Returns new Hash without nil values.", 5], - ["compact!", "compact!", "Removes all nil value from self.", 5], - ["delete", "delete(key)", "Deletes the key-value pair and returns the value, otherwise returns nil.", 5], - ["delete", "delete(key, &block)", "Deletes the key-value pair and returns the value, else yields key with given block.", 5], - ["delete_if", "delete_if(&block)", "Deletes each key-value pair for which the given block returns true.", 5], - ["dup", "dup", "Duplicates a Hash.", 5], - ["each", "each", "Returns an iterator over the hash entries.", 5], - ["each", "each(&block) : Nil", "Calls the given block for each key-value pair and passes in the key and the value.", 5], - ["each_key", "each_key", "Returns an iterator over the hash keys.", 5], - ["each_key", "each_key(&block)", "Calls the given block for each key-value pair and passes in the key.", 5], - ["each_value", "each_value(&block)", "Calls the given block for each key-value pair and passes in the value.", 5], - ["each_value", "each_value", "Returns an iterator over the hash values.", 5], - ["empty?", "empty?", "Returns true when hash contains no key-value pairs.", 5], - ["fetch", "fetch(key, &block)", "Returns the value for the key given by key, or when not found calls the given block with the key.", 5], - ["fetch", "fetch(key, default)", "Returns the value for the key given by key, or when not found the value given by default.", 5], - ["fetch", "fetch(key)", "Returns the value for the key given by key.", 5], - ["first_key", "first_key", "Returns the first key in the hash.", 5], - ["first_key?", "first_key?", "Returns the first key if it exists, or returns nil.", 5], - ["first_value", "first_value", "Returns the first value in the hash.", 5], - ["first_value?", "first_value?", "Similar to #first_key?, but returns its value.", 5], - ["has_key?", "has_key?(key)", "Returns true when key given by key exists, otherwise false.", 5], - ["has_value?", "has_value?(val)", "Returns true when value given by value exists, otherwise false.", 5], - ["hash", "hash", "See also: Object#hash.", 5], - ["invert", "invert", "Inverts keys and values.", 5], - ["key", "key(value)", "Returns the first key with the given value, else raises KeyError.", 5], - ["key", "key(value, &block)", "Returns the first key with the given value, else yields value with the given block.", 5], - ["key?", "key?(value)", "Returns the first key with the given value, else nil.", 5], - ["key_index", "key_index(key)", "Returns the index of the given key, or nil when not found.", 5], - ["keys", "keys", "Returns a new Array with all the keys.", 5], - ["merge", "merge(other : Hash(L, W)) forall L, W", "Returns a new Hash with the keys and values of this hash and other combined.", 5], - ["merge!", "merge!(other : Hash)", "Similar to #merge, but the receiver is modified.", 5], - ["reject", "reject(*keys)", "Returns a new Hash without the given keys.", 5], - ["reject", "reject(&block : K, V -> _)", "Returns a new hash consisting of entries for which the block returns false.", 5], - ["reject!", "reject!(&block : K, V -> _)", "Equivalent to Hash#reject, but makes modification on the current object rather that returning a new one.", 5], - ["reject!", "reject!(keys : Array | Tuple)", "Removes a list of keys out of hash.", 5], - ["select", "select(&block : K, V -> _)", "Returns a new hash consisting of entries for which the block returns true.", 5], - ["select", "select(keys : Array | Tuple)", "Returns a new Hash with the given keys.", 5], - ["select!", "select!(&block : K, V -> _)", "Equivalent to Hash#select but makes modification on the current object rather that returning a new one.", 5], - ["select!", "select!(keys : Array | Tuple)", "Removes every element except the given ones.", 5], - ["shift", "shift(&block)", "Deletes and returns the first key-value pair in the hash.", 5], - ["shift", "shift", "Deletes and returns the first key-value pair in the hash, or raises IndexError if the hash is empty.", 5], - ["shift?", "shift?", "Same as #shift, but returns nil if the hash is empty.", 5], - ["to_h", "to_h", "Returns self.", 5], - ["to_s", "to_s(io : IO)", "Converts to a String.", 5], - ["values", "values", "Returns only the values as an Array.", 5], - ["values_at", "values_at(*indexes : K)", "Returns a tuple populated with the elements at the given indexes.", 5] -] -export const RANGE_METHODS = [ - ["===", "===(value)", "Same as #includes?, useful for the case expression."], - ["begin", "begin : B", "Returns the object that defines the beginning of this range."], - ["bsearch", "bsearch(&block)", "By using binary search, returns the first value for which the passed block returns true."], - ["clone", "clone", "Returns a new Range with #begin and #end cloned."], - ["covers?", "covers?(value)", "Same as #includes?."], - ["cycle", "cycle", "Returns an Iterator that cycles over the values of this range."], - ["each", "each(&block) : Nil", "Iterates over the elements of this range, passing each in turn to the block."], - ["each", "each", "Returns an Iterator over the elements of this range."], - ["end", "end : E", "Returns the object that defines the end of the range."], - ["excludes_end?", "excludes_end?", "Returns true if this range excludes the end element."], - ["exclusive?", "exclusive? : Bool", "Returns true if the range is exclusive."], - ["includes?", "includes?(value)", "Returns true if this range includes the given value."], - ["reverse_each", "reverse_each(&block) : Nil", "Iterates over the elements of this range in reverse order, passing each in turn to the block."], - ["reverse_each", "reverse_each", "Returns a reverse Iterator over the elements of this range."], - ["step", "step(by = 1)", "Returns an Iterator that returns each nth element in this range."], - ["step", "step(by = 1, &block)", "Iterates over this range, passing each nth element to the block."], - ["sum", "sum(initial)", "If self is a Int range, it provides O(1) implementation, otherwise it is same as Enumerable#sum."], -] -export const REGEX_METHODS = [ - ["error?", "error?(source)", "Determines Regex's source validity."], - ["escape", "escape(str) : String", "Returns a String constructed by escaping any metacharacters in str."], - ["union", "union(patterns : Enumerable(Regex | String)) : self", "Union."], - ["union", "union(*patterns : Regex | String) : self", "Union."], - ["arity", "arity", "Returns the number of arguments of this Proc."], - ["call", "call(*args : *T) : R", "Invokes this Proc and returns the result."], - ["partial", "partial(*args : *U) forall U", "Returns a new Proc that has its first arguments fixed to the values given by args."], - ["arity", "arity", "Returns the number of arguments of this Proc."], - ["call", "call(*args : *T) : R", "Invokes this Proc and returns the result."], - ["partial", "partial(*args : *U) forall U", "Returns a new Proc that has its first arguments fixed to the values given by args."], - ["arity", "arity", "Returns the number of arguments of this Proc."], - ["call", "call(*args : *T) : R", "Invokes this Proc and returns the result."], - ["partial", "partial(*args : *U) forall U", "Returns a new Proc that has its first arguments fixed to the values given by args."], - ["arity", "arity", "Returns the number of arguments of this Proc."], - ["call", "call(*args : *T) : R", "Invokes this Proc and returns the result."], - ["partial", "partial(*args : *U) forall U", "Returns a new Proc that has its first arguments fixed to the values given by args."], - ["arity", "arity", "Returns the number of arguments of this Proc."], - ["call", "call(*args : *T) : R", "Invokes this Proc and returns the result."], - ["partial", "partial(*args : *U) forall U", "Returns a new Proc that has its first arguments fixed to the values given by args."], - ["arity", "arity", "Returns the number of arguments of this Proc."], - ["call", "call(*args : *T) : R", "Invokes this Proc and returns the result."], - ["partial", "partial(*args : *U) forall U", "Returns a new Proc that has its first arguments fixed to the values given by args."], - ["arity", "arity", "Returns the number of arguments of this Proc."], - ["call", "call(*args : *T) : R", "Invokes this Proc and returns the result."], - ["partial", "partial(*args : *U) forall U", "Returns a new Proc that has its first arguments fixed to the values given by args."], - ["arity", "arity", "Returns the number of arguments of this Proc."], - ["call", "call(*args : *T) : R", "Invokes this Proc and returns the result."], - ["partial", "partial(*args : *U) forall U", "Returns a new Proc that has its first arguments fixed to the values given by args."], - ["arity", "arity", "Returns the number of arguments of this Proc."], - ["call", "call(*args : *T) : R", "Invokes this Proc and returns the result."], - ["partial", "partial(*args : *U) forall U", "Returns a new Proc that has its first arguments fixed to the values given by args."], - ["arity", "arity", "Returns the number of arguments of this Proc."], - ["call", "call(*args : *T) : R", "Invokes this Proc and returns the result."], - ["partial", "partial(*args : *U) forall U", "Returns a new Proc that has its first arguments fixed to the values given by args."], - ["arity", "arity", "Returns the number of arguments of this Proc."], - ["call", "call(*args : *T) : R", "Invokes this Proc and returns the result."], - ["partial", "partial(*args : *U) forall U", "Returns a new Proc that has its first arguments fixed to the values given by args."], - ["==", "==(other : self)", ""], - ["===", "===(other : self)", ""], - ["===", "===(other)", ""], - ["clone", "clone", ""], - ["closure?", "closure?", ""], - ["closure_data", "closure_data", ""], - ["hash", "hash", ""], - ["pointer", "pointer", ""], - ["to_s", "to_s(io)", ""], - ["==", "==(other : self)", ""], - ["===", "===(other : self)", ""], - ["===", "===(other)", ""], - ["clone", "clone", ""], - ["closure?", "closure?", ""], - ["closure_data", "closure_data", ""], - ["hash", "hash", ""], - ["pointer", "pointer", ""], - ["to_s", "to_s(io)", ""], - ["==", "==(other : self)", ""], - ["===", "===(other : self)", ""], - ["===", "===(other)", ""], - ["clone", "clone", ""], - ["closure?", "closure?", ""], - ["closure_data", "closure_data", ""], - ["hash", "hash", ""], - ["pointer", "pointer", ""], - ["to_s", "to_s(io)", ""], - ["==", "==(other : self)", ""], - ["===", "===(other : self)", ""], - ["===", "===(other)", ""], - ["clone", "clone", ""], - ["closure?", "closure?", ""], - ["closure_data", "closure_data", ""], - ["hash", "hash", ""], - ["pointer", "pointer", ""], - ["to_s", "to_s(io)", ""], - ["==", "==(other : self)", ""], - ["===", "===(other : self)", ""], - ["===", "===(other)", ""], - ["clone", "clone", ""], - ["closure?", "closure?", ""], - ["closure_data", "closure_data", ""], - ["hash", "hash", ""], - ["pointer", "pointer", ""], - ["to_s", "to_s(io)", ""], - ["==", "==(other : self)", ""], - ["===", "===(other : self)", ""], - ["===", "===(other)", ""], - ["clone", "clone", ""], - ["closure?", "closure?", ""], - ["closure_data", "closure_data", ""], - ["hash", "hash", ""], - ["pointer", "pointer", ""], - ["to_s", "to_s(io)", ""], - ["==", "==(other : self)", ""], - ["===", "===(other : self)", ""], - ["===", "===(other)", ""], - ["clone", "clone", ""], - ["closure?", "closure?", ""], - ["closure_data", "closure_data", ""], - ["hash", "hash", ""], - ["pointer", "pointer", ""], - ["to_s", "to_s(io)", ""], - ["==", "==(other : self)", ""], - ["===", "===(other : self)", ""], - ["===", "===(other)", ""], - ["clone", "clone", ""], - ["closure?", "closure?", ""], - ["closure_data", "closure_data", ""], - ["hash", "hash", ""], - ["pointer", "pointer", ""], - ["to_s", "to_s(io)", ""], - ["==", "==(other : self)", ""], - ["===", "===(other : self)", ""], - ["===", "===(other)", ""], - ["clone", "clone", ""], - ["closure?", "closure?", ""], - ["closure_data", "closure_data", ""], - ["hash", "hash", ""], - ["pointer", "pointer", ""], - ["to_s", "to_s(io)", ""], - ["==", "==(other : self)", ""], - ["===", "===(other : self)", ""], - ["===", "===(other)", ""], - ["clone", "clone", ""], - ["closure?", "closure?", ""], - ["closure_data", "closure_data", ""], - ["hash", "hash", ""], - ["pointer", "pointer", ""], - ["to_s", "to_s(io)", ""], - ["==", "==(other : self)", ""], - ["===", "===(other : self)", ""], - ["===", "===(other)", ""], - ["clone", "clone", ""], - ["closure?", "closure?", ""], - ["closure_data", "closure_data", ""], - ["hash", "hash", ""], - ["pointer", "pointer", ""], - ["to_s", "to_s(io)", ""] -] -export const TUPLE_METHODS = [ - ["join", "join(separator, io)", "Prints to io all the elements in the collection, separated by separator.", 5], - ["join", "join(separator = \"\")", "Returns a String created by concatenating the elements in the collection, separated by separator (defaults to none).", 5], - ["join", "join(separator, io, &block)", "Prints to io the concatenation of the elements, with the possibility of controlling how the printing is done via a block.", 5], - ["join", "join(separator = \"\", &block)", "Returns a String created by concatenating the results of passing the elements in the collection to the passed block, separated by separator (defaults to none).", 5], - ["from", "from(array : Array)", "Creates a tuple from the given array, with elements casted to the given types."], - ["+", "+(other : Tuple)", "Returns a tuple that contains self's elements followed by other's elements."], - ["<=>", "<=>(other : self)", "Implements the comparison operator."], - ["<=>", "<=>(other : Tuple)", "Implements the comparison operator."], - ["==", "==(other : self)", "Returns true if this tuple has the same size as the other tuple and their elements are equal to each other when compared with #==."], - ["==", "==(other : Tuple)", "Returns true if this tuple has the same size as the other tuple and their elements are equal to each other when compared with #==."], - ["===", "===(other : self)", "Returns true if case equality holds for the elements in self and other."], - ["===", "===(other : Tuple)", "Returns true if self and other have the same size and case equality holds for the elements in self and other."], - ["[]", "[](index : Int)", "Returns the element at the given index."], - ["[]?", "[]?(index : Int)", "Returns the element at the given index or nil if out of bounds."], - ["at", "at(index : Int)", "Returns the element at the given index or raises IndexError if out of bounds."], - ["at", "at(index : Int, &block)", "Returns the element at the given index or the value returned by the block if out of bounds."], - ["clone", "clone", "Returns a tuple containing cloned elements of this tuple using the #clone method."], - ["each", "each(&block) : Nil", "Yields each of the elements in this tuple."], - ["first", "first", "Returns the first element of this tuple."], - ["first?", "first?", "Returns the first element of this tuple, or nil if this is the empty tuple."], - ["from", "from(array : Array)", "Expects to be called on a tuple of types, creates a tuple from the given array, with types casted appropriately."], - ["hash", "hash", "Returns a hash value based on this tuple's length and contents."], - ["inspect", "inspect", "Same as #to_s."], - ["last", "last", "Returns the last element of this tuple."], - ["last?", "last?", "Returns the last element of this tuple, or nil if this is the empty tuple."], - ["map", "map(&block)", "Returns a new tuple where elements are mapped by the given block."], - ["reverse", "reverse", "Returns a new tuple where the elements are in reverse order."], - ["reverse_each", "reverse_each(&block)", "Yields each of the elements in this tuple in reverse order."], - ["size", "size", "Returns the number of elements in this tuple."], - ["to_s", "to_s(io)", "Appends a string representation of this tuple to the given IO."], - ["types", "types", "Returns the types of this tuple."], - ["==", "==(other)", ""], - ["pretty_print", "pretty_print(pp) : Nil", ""], - ["to_json", "to_json(json : JSON::Builder)", ""], - ["to_yaml", "to_yaml(yaml : YAML::Builder)", ""], - ["unsafe_at", "unsafe_at(index : Int)", ""] -] -export const NAMEDTUPLE_METHODS = [ - ["from", "from(hash : Hash)", "Creates a named tuple from the given hash, with elements casted to the given types."], - ["==", "==(other : NamedTuple)", "Returns true if this tuple has the same keys as other, and values for each key are the same in self and other."], - ["==", "==(other : self)", "Returns true if this tuple has the same keys as other, and values for each key are the same in self and other."], - ["[]", "[](key : Symbol | String)", "Returns the value for the given key, if there's such key, otherwise raises KeyError."], - ["[]?", "[]?(key : Symbol | String)", "Returns the value for the given key, if there's such key, otherwise returns nil."], - ["clone", "clone", "Returns a named tuple with the same keys but with cloned values, using the #clone method."], - ["each", "each(&block) : Nil", "Yields each key and value in this named tuple."], - ["each_key", "each_key(&block) : Nil", "Yields each key in this named tuple."], - ["each_value", "each_value(&block) : Nil", "Yields each value in this named tuple."], - ["each_with_index", "each_with_index(offset = 0, &block)", "Yields each key and value, together with an index starting at offset, in this named tuple."], - ["empty?", "empty?", "Returns true if this named tuple is empty."], - ["fetch", "fetch(key : Symbol | String, default_value)", "Returns the value for the given key, if there's such key, otherwise returns default_value."], - ["fetch", "fetch(key : Symbol, &block)", "Returns the value for the given key, if there's such key, otherwise the value returned by the block."], - ["fetch", "fetch(key : String, &block)", "Returns the value for the given key, if there's such key, otherwise the value returned by the block."], - ["from", "from(hash : Hash)", "Expects to be called on a named tuple whose values are types, creates a tuple from the given hash, with types casted appropriately."], - ["has_key?", "has_key?(key : String) : Bool", "Returns true if this named tuple has the given key, false otherwise."], - ["has_key?", "has_key?(key : Symbol) : Bool", "Returns true if this named tuple has the given key, false otherwise."], - ["hash", "hash", "Returns a hash value based on this name tuple's size, keys and values."], - ["inspect", "inspect", "Same as #to_s."], - ["keys", "keys", "Returns a Tuple of symbols with the keys in this named tuple."], - ["map", "map(&block)", "Returns an Array populated with the results of each iteration in the given block, which is given each key and value in this named tuple."], - ["size", "size", "Returns the number of elements in this named tuple."], - ["to_a", "to_a", "Returns a new Array of tuples populated with each key-value pair."], - ["to_h", "to_h", "Returns a Hash with the keys and values in this named tuple."], - ["to_s", "to_s(io)", "Appends a string representation of this named tuple to the given IO."], - ["values", "values", "Returns a Tuple with the values in this named tuple."], - ["pretty_print", "pretty_print(pp)", ""], - ["to_json", "to_json(json : JSON::Builder)", ""], - ["to_yaml", "to_yaml(yaml : YAML::Builder)", ""] -] -export const PROC_METHODS = [ - ["==", "==(other : self)", ""], - ["===", "===(other : self)", ""], - ["===", "===(other)", ""], - ["clone", "clone", ""], - ["closure?", "closure?", ""], - ["closure_data", "closure_data", ""], - ["hash", "hash", ""], - ["pointer", "pointer", ""], - ["to_s", "to_s(io)", ""], - ["arity", "arity", "Returns the number of arguments of this Proc."], - ["call", "call(*args : *T) : R", "Invokes this Proc and returns the result."], - ["partial", "partial(*args : *U) forall U", "Returns a new Proc that has its first arguments fixed to the values given by args."] -] -export const TOP_LEVEL_METHODS = [ - ["`", "`(command) : String", "Returns the standard output of executing command in a subshell."], - ["abort", "abort(message, status = 1) : NoReturn", "Terminates execution immediately, printing message to STDERR and then calling exit(status)."], - ["at_exit", "at_exit(&handler : Int32 -> ) : Nil", "Registers the given Proc for execution when the program exits."], - ["delay", "delay(delay, &block : -> _)", "Spawns a Fiber to compute &block in the background after delay has elapsed."], - ["exit", "exit(status = 0) : NoReturn", "Terminates execution immediately, returning the given status code to the invoking environment."], - ["fork", "fork", "See also: Process.fork"], - ["fork", "fork(&block)", "See also: Process.fork"], - ["future", "future(&exp : -> _)", "Spawns a Fiber to compute &block in the background."], - ["gets", "gets(*args, **options)", "Reads a line from STDIN."], - ["lazy", "lazy(&block : -> _)", "Conditionally spawns a Fiber to run &block in the background."], - ["loop", "loop(&block)", "Repeatedly executes the block, passing an incremental Int32 that starts with 0."], - ["p", "p(*objects)", "Pretty prints each object in objects to STDOUT, followed by a newline."], - ["p", "p", "Pretty prints each object in objects to STDOUT, followed by a newline."], - ["p", "p(object)", "Pretty prints object to STDOUT followed by a newline."], - ["print", "print(*objects : _) : Nil", "Prints objects to STDOUT and then invokes STDOUT.flush."], - ["printf", "printf(format_string, args : Array | Tuple) : Nil", "Prints a formatted string to STDOUT."], - ["printf", "printf(format_string, *args) : Nil", "Prints a formatted string to STDOUT."], - ["puts", "puts(*objects) : Nil", "Prints objects to STDOUT, each followed by a newline."], - ["rand", "rand(x)", "See Random#rand(x)."], - ["rand", "rand", "See Random#rand."], - ["read_line", "read_line(*args, **options)", "Reads a line from STDIN."], - ["sleep", "sleep", "Blocks the current fiber forever."], - ["sleep", "sleep(time : Time::Span)", "Blocks the current Fiber for the specified time span."], - ["sleep", "sleep(seconds : Number)", "Blocks the current fiber for the specified number of seconds."], - ["spawn", "spawn(*, name : String? = nil, &block)", "Spawns a new fiber."], - ["sprintf", "sprintf(format_string, *args) : String", "Returns a formatted string."], - ["sprintf", "sprintf(format_string, args : Array | Tuple) : String", "Returns a formatted string."], - ["system", "system(command : String, args = nil) : Bool", "Executes the given command in a subshell."], - ["pp", "pp(*exps)", "Prints a series of expressions together with their values."], - ["record", "record(name, *properties)", "Defines a Struct with the given name and properties."], - ["spawn", "spawn(call, *, name = nil)", "Spawns a fiber by first creating a Proc, passing the call's expressions to it, and letting the Proc finally invoke the call."], - ["caller", "caller", ""], - ["raise", "raise(ex : Exception) : NoReturn", ""], - ["raise", "raise(message : String) : NoReturn", ""], - ["with_color", "with_color(color : Symbol)", ""], - ["with_color", "with_color", ""], - ["assert_responds_to", "assert_responds_to(var, method)", ""], - ["debugger", "debugger", ""], - ["parallel", "parallel(*jobs)", ""], - ["redefine_main", "redefine_main(name = main)", ""] -] -export const STRUCTS = [ - ["Atomic", "struct Atomic(T)", ""], - ["BigFloat", "struct BigFloat", ""], - ["BigInt", "struct BigInt", ""], - ["BigRational", "struct BigRational", ""], - ["BitArray", "struct BitArray", ""], - ["Bool", "struct Bool", ""], - ["Char", "struct Char", ""], - ["Complex", "struct Complex", ""], - ["Enum", "abstract struct Enum", ""], - ["Float", "abstract struct Float", ""], - ["Float32", "struct Float32", ""], - ["Float64", "struct Float64", ""], - ["Int", "abstract struct Int", ""], - ["Int16", "struct Int16", ""], - ["Int32", "struct Int32", ""], - ["Int64", "struct Int64", ""], - ["Int8", "struct Int8", ""], - ["NamedTuple", "struct NamedTuple(**T)", ""], - ["Nil", "struct Nil", ""], - ["Number", "abstract struct Number", ""], - ["Pointer", "struct Pointer(T)", ""], - ["Proc", "struct Proc(*T, R)", ""], - ["Range", "struct Range(B, E)", ""], - ["Reflect", "struct Reflect(X)", ""], - ["Set", "struct Set(T)", ""], - ["Slice", "struct Slice(T)", ""], - ["StaticArray", "struct StaticArray(T, N)", ""], - ["Struct", "abstract struct Struct", ""], - ["Symbol", "struct Symbol", ""], - ["Time", "struct Time", ""], - ["Toplevel", "Top Level Namespace", ""], - ["Tuple", "struct Tuple(*T)", ""], - ["UInt16", "struct UInt16", ""], - ["UInt32", "struct UInt32", ""], - ["UInt64", "struct UInt64", ""], - ["UInt8", "struct UInt8", ""], - ["Union", "struct Union(*T)", ""], - ["Value", "abstract struct Value", ""] -] -export const FILE_METHODS = [ - ["basename", "basename(path) : String", "Returns the last component of the given path."], - ["basename", "basename(path, suffix) : String", "Returns the last component of the given path."], - ["chmod", "chmod(path, mode : Int)", "Changes the permissions of the specified file."], - ["chown", "chown(path, uid : Int? = -1, gid : Int = -1, follow_symlinks = false)", "Changes the owner of the specified file."], - ["delete", "delete(path)", "Delete the file at path."], - ["directory?", "directory?(path) : Bool", "Returns true if the given path exists and is a directory."], - ["dirname", "dirname(path) : String", "Returns all components of the given path except the last one."], - ["each_line", "each_line(filename, encoding = nil, invalid = nil, chomp = true, &block)", "Yields each line in filename to the given block."], - ["each_line", "each_line(filename, encoding = nil, invalid = nil, chomp = true)", "Returns an Iterator for each line in filename."], - ["empty?", "empty?(path) : Bool", "Returns true if the file at path is empty, otherwise returns false."], - ["executable?", "executable?(path) : Bool", "Returns true if path is executable by the real user id of this process else returns false."], - ["exists?", "exists?(path) : Bool", "Returns true if path exists else returns false"], - ["expand_path", "expand_path(path, dir = nil) : String", "Converts path to an absolute path."], - ["extname", "extname(filename) : String", "Returns filename's extension, or an empty string if it has no extension."], - ["file?", "file?(path) : Bool", "Returns true if given path exists and is a file."], - ["join", "join(parts : Array | Tuple) : String", "Returns a new string formed by joining the strings using File::SEPARATOR."], - ["join", "join(*parts) : String", "Returns a new string formed by joining the strings using File::SEPARATOR."], - ["link", "link(old_path, new_path)", "Creates a new link (also known as a hard link) at new_path to an existing file given by old_path."], - ["lstat", "lstat(path) : Stat", "Returns a File::Stat object for the file given by path or raises Errno in case of an error."], - ["open", "open(filename, mode = 'r', perm = DEFAULT_CREATE_MODE, encoding = nil, invalid = nil, &block)", "Opens the file named by filename."], - ["read", "read(filename, encoding = nil, invalid = nil) : String", "Returns the content of filename as a string."], - ["read_lines", "read_lines(filename, encoding = nil, invalid = nil, chomp = true) : Array(String)", "Returns all lines in filename as an array of strings."], - ["readable?", "readable?(path) : Bool", "Returns true if path is readable by the real user id of this process else returns false."], - ["real_path", "real_path(path) : String", "Resolves the real path of path by following symbolic links."], - ["rename", "rename(old_filename, new_filename)", "Moves old_filename to new_filename."], - ["size", "size(filename) : UInt64", "Returns the size of filename bytes."], - ["stat", "stat(path) : Stat", "Returns a File::Stat object for the file given by path or raises Errno in case of an error."], - ["symlink", "symlink(old_path, new_path)", "Creates a symbolic link at new_path to an existing file given by *old_path."], - ["symlink?", "symlink?(path) : Bool", "Returns true if the path is a symbolic link."], - ["touch", "touch(filename : String, time : Time = Time.now)", "Attempts to set the access and modification times of the file named in the filename parameter to the value given in time."], - ["utime", "utime(atime : Time, mtime : Time, filename : String) : Nil", "Sets the access and modification times of filename."], - ["writable?", "writable?(path) : Bool", "Returns true if path is writable by the real user id of this process else returns false."], - ["write", "write(filename, content, perm = DEFAULT_CREATE_MODE, encoding = nil, invalid = nil)", "Write the given content to filename."] -] -export const DIR_METHODS = [ - ["cd", "cd(path)", "Changes the current working directory of the process to the given string."], - ["cd", "cd(path, &block)", "Changes the current working directory of the process to the given string and invokes the block, restoring the original working directory when the block exits."], - ["current", "current : String", "Returns the current working directory."], - ["empty?", "empty?(path) : Bool", "Returns true if the directory at path is empty, otherwise returns false."], - ["entries", "entries(dirname) : Array(String)", "Returns an array containing all of the filenames in the given directory."], - ["exists?", "exists?(path) : Bool", "Returns true if the given path exists and is a directory"], - ["foreach", "foreach(dirname, &block)", "Calls the block once for each entry in the named directory, passing the filename of each entry as a parameter to the block."], - ["mkdir", "mkdir(path, mode = 511)", "Creates a new directory at the given path."], - ["mkdir_p", "mkdir_p(path, mode = 511)", "Creates a new directory at the given path, including any non-existing intermediate directories."], - ["open", "open(path, &block)", "Opens a directory and yields it, closing it at the end of the block."], - ["rmdir", "rmdir(path)", "Removes the directory at the given path."], - ["[]", "[](*patterns) : Array(String)", ""], - ["[]", "[](patterns : Enumerable(String)) : Array(String)", ""], - ["glob", "glob(patterns : Enumerable(String), &block)", ""], - ["glob", "glob(*patterns) : Array(String)", ""], - ["glob", "glob(patterns : Enumerable(String)) : Array(String)", ""], - ["glob", "glob(*patterns, &block)", ""] -] -export const CHANNEL_METHODS = [ - ["close", "close", ""], - ["closed?", "closed?", ""], - ["inspect", "inspect(io)", ""], - ["pretty_print", "pretty_print(pp)", ""], - ["receive", "receive", ""], - ["receive?", "receive?", ""], - ["receive_select_action", "receive_select_action", ""], - ["send_select_action", "send_select_action(value : T)", ""], - ["unwait_for_receive", "unwait_for_receive", ""], - ["unwait_for_send", "unwait_for_send", ""], - ["wait_for_receive", "wait_for_receive", ""], - ["wait_for_send ", "wait_for_send ", ""] -] -export const CLASSES = [ - ["Argumenterror", "class ArgumentError", ""], - ["Array", "class Array(T)", ""], - ["Box", "class Box(T)", ""], - ["Channel", "abstract class Channel(T)", ""], - ["Class", "abstract class Class", ""], - ["Csv", "class CSV", ""], - ["Deque", "class Deque(T)", ""], - ["Dir", "class Dir", ""], - ["Divisionbyzero", "class DivisionByZero", ""], - ["Errno", "class Errno", ""], - ["Exception", "class Exception", ""], - ["Fiber", "class Fiber", ""], - ["File", "class File", ""], - ["Hash", "class Hash(K, V)", ""], - ["Indexerror", "class IndexError", ""], - ["Ini", "class INI", ""], - ["Invalidbytesequenceerror", "class InvalidByteSequenceError", ""], - ["Ipsocket", "class IPSocket", ""], - ["Keyerror", "class KeyError", ""], - ["Logger", "class Logger", ""], - ["Markdown", "class Markdown", ""], - ["Mutex", "class Mutex", ""], - ["Object", "abstract class Object", ""], - ["Optionparser", "class OptionParser", ""], - ["Prettyprint", "class PrettyPrint", ""], - ["Process", "class Process", ""], - ["Reference", "class Reference", ""], - ["Regex", "class Regex", ""], - ["Socket", "class Socket", ""], - ["String", "class String", ""], - ["Stringpool", "class StringPool", ""], - ["Stringscanner", "class StringScanner", ""], - ["Tcpserver", "class TCPServer", ""], - ["Tcpsocket", "class TCPSocket", ""], - ["Tempfile", "class Tempfile", ""], - ["Typecasterror", "class TypeCastError", ""], - ["Udpsocket", "class UDPSocket", ""], - ["Unixserver", "class UNIXServer", ""], - ["Unixsocket", "class UNIXSocket", ""], - ["Uri", "class URI", ""], - ["Weakref", "class WeakRef(T)", ""] -] -export const MODULES = [ - ["Adler32", "module Adler32", ""], - ["Base64", "module Base64", ""], - ["Benchmark", "module Benchmark", ""], - ["Colorize", "module Colorize", ""], - ["Comparable", "module Comparable(T)", ""], - ["Concurrent", "module Concurrent", ""], - ["Crc32", "module CRC32", ""], - ["Crypto", "module Crypto", ""], - ["Crystal", "module Crystal", ""], - ["Digest", "module Digest", ""], - ["Dl", "module DL", ""], - ["Ecr", "module ECR", ""], - ["Enumerable", "module Enumerable(T)", ""], - ["Env", "module ENV", ""], - ["Fileutils", "module FileUtils", ""], - ["Flate", "module Flate", ""], - ["Gc", "module GC", ""], - ["Gzip", "module Gzip", ""], - ["Html", "module HTML", ""], - ["Http", "module HTTP", ""], - ["Indexable", "module Indexable(T)", ""], - ["Io", "module IO", ""], - ["Iterable", "module Iterable(T)", ""], - ["Iterator", "module Iterator(T)", ""], - ["Json", "module JSON", ""], - ["Levenshtein", "module Levenshtein", ""], - ["Llvm", "module LLVM", ""], - ["Math", "module Math", ""], - ["Oauth", "module OAuth", ""], - ["Oauth2", "module OAuth2", ""], - ["Openssl", "module OpenSSL", ""], - ["Partialcomparable", "module PartialComparable(T)", ""], - ["Random", "module Random", ""], - ["Readline", "module Readline", ""], - ["Securerandom", "module SecureRandom", ""], - ["Spec", "module Spec", ""], - ["System", "module System", ""], - ["Termios", "module Termios", ""], - ["Unicode", "module Unicode", ""], - ["Xml", "module XML", ""], - ["Yaml", "module YAML", ""], - ["Zip", "module Zip", ""], - ["Zlib", "module Zlib", ""] -] -export const ALIAS = [ - ["Bytes", "alias Bytes", ""] -] -export const ENUMS = [ - ["Signal", "enum Signal", ""] -] diff --git a/src/crystalContext.ts b/src/crystalContext.ts deleted file mode 100755 index c487dc6..0000000 --- a/src/crystalContext.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as vscode from "vscode" -import { spawn } from "child_process" - -import { spawnTools } from "./crystalUtils" - -/** - * Call tool for get Crystal context - */ -export class CrystalContext { - - /** - * Execute crystal tool context for current file:position - */ - crystalContext(document, position, key) { - return spawnTools(document, position, "context", key) - } -} \ No newline at end of file diff --git a/src/crystalDiagnostic.ts b/src/crystalDiagnostic.ts deleted file mode 100755 index 733bf09..0000000 --- a/src/crystalDiagnostic.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as vscode from "vscode" - -import { Concurrent, isNotLib, spawnCompiler } from "./crystalUtils" - -/** - * Execute crystal build to check problems. - */ -export function getDiagnostic(document: vscode.TextDocument) { - const config = vscode.workspace.getConfiguration("crystal-lang") - if (config["problems"] == "syntax") { - return spawnCompiler(document, false) - } else if (Concurrent.counter < Concurrent.limit() && config["problems"] == "build" && isNotLib(document.fileName)) { - return spawnCompiler(document, true) - } else if (Concurrent.counter >= Concurrent.limit()) { - console.info("INFO: crystal is taking a moment to build") - } -} diff --git a/src/crystalFormatting.ts b/src/crystalFormatting.ts deleted file mode 100755 index f137112..0000000 --- a/src/crystalFormatting.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as vscode from "vscode" -import { spawn } from "child_process" - -import { searchProblemsFromRaw, childOnStd, childOnError } from "./crystalUtils" - -/** - * Formatting provider using VSCode module - */ -export class CrystalFormattingProvider implements vscode.DocumentFormattingEditProvider { - - /** - * Execute crystal tool format and get response. - * Returns tuple of [stdout, stderr] texts. - */ - execFormat(document: vscode.TextDocument) { - return new Promise<[string, string]>(function (resolve, reject) { - let responseOut = "" - let responseErr = "" - const config = vscode.workspace.getConfiguration("crystal-lang") - let child = spawn(`${config["compiler"]}`, ["tool", "format", "--no-color", "-"]) - child.stdin.write(document.getText()) - child.stdin.end() - child.stdout.setEncoding('utf-8') - childOnError(child) - child.stdout.on("data", (data) => { - responseOut += data - }); - child.stderr.on("data", (data) => { - responseErr += data - }); - childOnStd(child, "end", () => { - return resolve([responseOut, responseErr]) - }) - }) - } - - /** - * Return formatted document to VSCode - */ - async provideDocumentFormattingEdits(document: vscode.TextDocument) { - let response = await this.execFormat(document) - let responseOut = response[0] - let responseErr = response[1] - let textEditData: vscode.TextEdit[] = [] - - // OnFly error check is disabled because -f json was removed from crystal, see: - // https://github.com/crystal-lang/crystal/pull/7257 (this is good reason to migrate to Scry :D) - // if ((searchProblems(response.toString(), document.uri).length == 0) && - // response.toString().length > 0) { - // } - - // QuickFix to replace current code with formated one only if no syntax error is found - if ((searchProblemsFromRaw(responseErr, document.uri).length == 0) && - responseOut.length > 0) { - let lastLineId = document.lineCount - 1 - let range = new vscode.Range(0, 0, lastLineId, document.lineAt(lastLineId).text.length) - textEditData = [vscode.TextEdit.replace(range, responseOut)] - } - - return textEditData - } -} diff --git a/src/crystalHover.ts b/src/crystalHover.ts deleted file mode 100644 index afa1716..0000000 --- a/src/crystalHover.ts +++ /dev/null @@ -1,138 +0,0 @@ -import * as vscode from "vscode" -import * as TDATA from "./crystalCompletionData" -import { CrystalContext } from "./crystalContext" -import { isNotKeyword, isNotLib, getSymbols } from "./crystalUtils" -import { deepEqual } from "assert"; - -// Basic Crystal information about types and methods -const TYPES = [ - TDATA.REFLECTION_METHODS, TDATA.NIL_METHODS, TDATA.BOOL_METHODS, TDATA.INT_METHODS, - TDATA.FLOAT_METHODS, TDATA.CHAR_METHODS, TDATA.STRING_METHODS, TDATA.SYMBOLS_METHODS, TDATA.ARRAY_METHODS, - TDATA.HASH_METHODS, TDATA.RANGE_METHODS, TDATA.REGEX_METHODS, TDATA.TUPLE_METHODS, TDATA.NAMEDTUPLE_METHODS, - TDATA.PROC_METHODS, TDATA.FILE_METHODS, TDATA.DIR_METHODS, TDATA.CHANNEL_METHODS, - TDATA.TOP_LEVEL_METHODS, TDATA.STRUCTS, TDATA.CLASSES, TDATA.MODULES, TDATA.ALIAS, TDATA.ENUMS -] - -/** - * Get information of hovered method or class - */ -export class CrystalHoverProvider extends CrystalContext implements vscode.HoverProvider { - - private position = new vscode.Position(0, 0) - - private equalPositions(stored, current) { - return (stored.line == current.line) && (stored.character == current.character) - } - - /** - * Return tooltip information to VSCode - */ - async provideHover(document: vscode.TextDocument, position: vscode.Position, token) { - const config = vscode.workspace.getConfiguration("crystal-lang") - if (!config["hover"] || this.equalPositions(this.position, position)) { - return - } - this.position = position - let line = document.getText(new vscode.Range(position.line, 0, position.line, position.character)) - // Check if line isn't a comment or string - let quotes = null - let comment = null - if (line) { - quotes = line.match(/(\")/g) - comment = line.match(/^[^\"]*#.*$/) - } - if (quotes == null && comment == null) { - let stop = false - let range = document.getWordRangeAtPosition(new vscode.Position(position.line, position.character)) - if (range) { - let word = document.getText(range) - if (isNotKeyword(word) && word.toLowerCase() == word && !parseInt(word)) { - // Checks for variables using context tool - let crystalOutput = await this.crystalContext(document, position, "hover") - if (crystalOutput.toString().startsWith(`{"status":"`)) { - try { - let crystalMessageObject = JSON.parse(crystalOutput.toString()) - if (crystalMessageObject.status == "ok") { - for (let element of crystalMessageObject.contexts) { - let type = element[word] - if (type) { - return new vscode.Hover(`${word} : ${type}`) - } - } - } else if (crystalMessageObject.status != "failed") { - stop = true - } - } catch (err) { - stop = true - console.error("ERROR: JSON.parse failed to parse crystal context output when hover") - throw err - } - } - } - if (isNotKeyword(word) && !parseInt(word) && !stop) { - // Checks Symbols - let symbols = await getSymbols(document.uri) - for (let symbol of symbols) { - if (symbol.name == word) { - let container = symbol.containerName ? ` of ${symbol.containerName}` : "" - return new vscode.Hover({ - language: "plaintext", - value: `${vscode.SymbolKind[symbol.kind]} ${symbol.name}${container}` - }) - } - } - // Checks completion data - let hoverTexts = [] - for (let type of TYPES) { - for (let element of type) { - if (element[0] == word) { - hoverTexts.push({ - language: "crystal", - value: `${element[1]}` - }) - if (element[2] !== "") { - hoverTexts.push({ - language: "plaintext", - value: `${element[2]}` - }) - } - } - } - } - // ------------------------------- - // TODO: Improve hover information - // ------------------------------- - return new vscode.Hover(filter(hoverTexts)) - } - } - } - } -} - -/** - * Remove duplicate methods and descriptions. - */ -function filter(hoverTexts: any[]) { - if (hoverTexts.length <= 1) { - return hoverTexts - } - let prev = false - return hoverTexts.filter((item, index, self) => { - if (index % 2 == 0) { - let objectIndex = self.findIndex((t) => { - return t.value == item.value - }) - let nextObjectIndex = self.findIndex((t) => { - if (self[index + 1] == undefined) { - return false - } else { - return t.value == self[index + 1].value - } - }) - prev = objectIndex == index && nextObjectIndex == index + 1 - return prev - } else { - return prev - } - }) -} diff --git a/src/crystalImplementations.ts b/src/crystalImplementations.ts deleted file mode 100755 index 793e1ee..0000000 --- a/src/crystalImplementations.ts +++ /dev/null @@ -1,79 +0,0 @@ -import * as vscode from "vscode" -import * as path from "path" -import * as fs from "fs" - -import { spawnTools, tryWindowsPath } from "./crystalUtils" - -/** - * Show implementations using VSCode provider - */ -export class CrystalImplementationsProvider implements vscode.DefinitionProvider { - - /** - * Execute crystal tool context for current file:position - */ - crystalImplementations(document: vscode.TextDocument, position: vscode.Position) { - return spawnTools(document, position, "impl", "implementations") - } - - /** - * Check if position is on local require (ex: require "../json") - */ - isLocalRequire(document: vscode.TextDocument, position: vscode.Position, line: String) { - let match = line.match(/^require\s*"([\.]{1,2}\/.*?)"\s*$/) - if (!match) { - return false - } - let capture = match.pop() - let word = document.getText(document.getWordRangeAtPosition(position)) - return capture.indexOf(word) > -1 - } - - /** - * Return location of local require - */ - getLocalLocation(document: vscode.TextDocument, line: String) { - let required = line.slice(line.indexOf("\"") + 1, line.lastIndexOf("\"")) - let dir = path.dirname(document.uri.path) - let location = path.join(dir, required + ".cr"); - if (!fs.existsSync(location)) { - return null - } - let expectedUri = vscode.Uri.file(location) - return new vscode.Location(expectedUri, new vscode.Position(0, 0)) - } - - /** - * Search for definitions in a Crystal project - */ - async provideDefinition(document: vscode.TextDocument, position: vscode.Position) { - let line = document.lineAt(position.line).text - if (this.isLocalRequire(document, position, line)) { - let location = this.getLocalLocation(document, line); - if (location) { - return location - } - } - let crystalOutput = await this.crystalImplementations(document, position) - let locations: vscode.Location[] = [] - if (crystalOutput.toString().startsWith(`{"status":"`)) { - try { - let crystalMessageObject = JSON.parse(crystalOutput.toString()) - if (crystalMessageObject.status == "ok") { - for (let element of crystalMessageObject.implementations) { - let file = tryWindowsPath(element.filename) - let position = new vscode.Position(element.line - 1, element.column - 1) - let location = new vscode.Location(vscode.Uri.file(file), position) - locations.push(location) - } - } else if (crystalMessageObject.status == "blocked") { - console.info("INFO: crystal is taking a moment to check implementation") - } - } catch (err) { - console.error("ERROR: JSON.parse failed to parse crystal implementations output") - throw err - } - } - return locations - } -} diff --git a/src/crystalMain.ts b/src/crystalMain.ts deleted file mode 100755 index a3a9b87..0000000 --- a/src/crystalMain.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as fs from "fs" -import * as vscode from "vscode" -import * as client from "vscode-languageclient" - -import { diagnosticCollection } from "./crystalUtils" -import { CrystalHoverProvider } from "./crystalHover" -import { getDiagnostic } from "./crystalDiagnostic" -import { CrystalFormattingProvider } from "./crystalFormatting" -import { CrystalDocumentSymbolProvider } from "./crystalSymbols" -import { CrystalCompletionItemProvider } from "./crystalCompletion" -import { CrystalImplementationsProvider } from "./crystalImplementations" -import { registerCrystalTask } from "./crystalTasks" - -// Language configuration for identation and patterns. Based on vscode-ruby -const crystalConfiguration = { - indentationRules: { - increaseIndentPattern: /^\s*((begin|(private\s+abstract|private|abstract)\s+(class|struct)|class|struct|(private|protected)\s+def|def|fun|macro|else|elsif|ensure|for|if|module|rescue|unless|until|when|in|while|case)|([^#]*\sdo\b)|([^#]*=\s*(case|if|unless)))\b([^#\{;]|("|'|\/).*\4)*(#.*)?$/, - decreaseIndentPattern: /^\s*([}\]]([,)]?\s*(#|$)|\.[a-zA-Z_]\w*\b)|(end|rescue|ensure|else|elsif|when|in)\b)/ - }, - wordPattern: /(-?\d+(?:\.\d+))|(:?[A-Za-z][^-`~@#%^&()=+[{}|;:'",<>/.*\]\s\\!?]*[!?]?)/ -} - -// VSCode identificator for Crystal -const CRYSTAL_MODE: client.DocumentSelector = [{ language: "crystal", scheme: "file" }]; - -/** - * Ensure to analyze only Crystal documents - */ -function diagnosticDocument(document) { - if (document.languageId == "crystal" && document.uri.scheme == "file") { - getDiagnostic(document) - } -} - -/** - * Init function for this extension - */ -export async function activate(context: vscode.ExtensionContext) { - // Call features not implemented on server yet. - context.subscriptions.push( - vscode.languages.setLanguageConfiguration("crystal", crystalConfiguration), - ) - - // Extension configuration - const config = vscode.workspace.getConfiguration("crystal-lang") - - // Register Tasks - registerCrystalTask(context) - - // Detect server and set configuration - let scry = config["server"] - if (fs.existsSync(scry)) { - let serverOptions = { command: scry, args: [] } - let clientOptions: client.LanguageClientOptions = { - documentSelector: CRYSTAL_MODE, - synchronize: { - configurationSection: "crystal-lang", - fileEvents: vscode.workspace.createFileSystemWatcher("**/*.cr") - } - } - let disposable = new client.LanguageClient("Crystal Language", serverOptions, clientOptions).start() - context.subscriptions.push(disposable) - } else { - // If server is disabled use Node.js implementation instead. - context.subscriptions.push( - diagnosticCollection, - vscode.languages.registerDocumentFormattingEditProvider(CRYSTAL_MODE, new CrystalFormattingProvider()), - vscode.workspace.onDidOpenTextDocument(diagnosticDocument), - vscode.workspace.onDidSaveTextDocument(diagnosticDocument), - vscode.languages.registerHoverProvider(CRYSTAL_MODE, new CrystalHoverProvider()), - vscode.languages.registerDocumentSymbolProvider(CRYSTAL_MODE, new CrystalDocumentSymbolProvider()), - vscode.languages.registerCompletionItemProvider(CRYSTAL_MODE, new CrystalCompletionItemProvider(), '.') - ) - if (config["implementations"]) { - context.subscriptions.push( - vscode.languages.registerDefinitionProvider(CRYSTAL_MODE, new CrystalImplementationsProvider()), - ) - } - } -} - -export function deactivate() { } diff --git a/src/crystalSymbols.ts b/src/crystalSymbols.ts deleted file mode 100755 index d4ef265..0000000 --- a/src/crystalSymbols.ts +++ /dev/null @@ -1,126 +0,0 @@ -import * as vscode from "vscode" - -/** - * Search document symbols using VSCode provider - */ -export class CrystalDocumentSymbolProvider implements vscode.DocumentSymbolProvider { - - private countEnds: number - private containers: any[] - private symbols: vscode.SymbolInformation[] - private document: vscode.TextDocument - - /** - * Create new symbol and append to symbols list - */ - newSymbol(name, kind, container, line, matchData) { - // ------------------- - // TODO: count columns - // ------------------- - let location = new vscode.Location(this.document.uri, new vscode.Position(line, 0)) - let symbolInfo = new vscode.SymbolInformation(name, kind, container, location) - this.symbols.push(symbolInfo) - } - - /** - * Set container for current symbol - */ - setContainer(matchData) { - if (this.containers[this.countEnds]) { - this.containers.pop() - } - this.containers.push(matchData) - this.countEnds += 1 - } - - /** - * Increment counter when an assignement expresion is found - */ - incrementEndsCountIfKeywordIn(element) { - const keywords = ["begin", "if", "case", "unless"] - for (let keyword of keywords) { - let keyFound = (() => { - if (keyword == "begin") { - return new RegExp(keyword + "$").test(element) - } - return new RegExp("= " + keyword + " .*$").test(element) - })() - if (keyFound) { - this.countEnds += 1 - break - } - } - } - - /** - * Return symbols result to VSCode - */ - async provideDocumentSymbols(document: vscode.TextDocument) { - let matrixText = document.getText().split("\n") - this.document = document - this.containers = [] - this.countEnds = 0 - this.symbols = [] - - // Search symbol line by line, ignoring comments and empty lines - for (let [index, element] of matrixText.entries()) { - let matchData: RegExpMatchArray - let comment = element.match(/^\s*#.*$/) - if (comment == null && element != "") { - if (matchData = element.match(/^\s*(abstract\s+)?(private\s+|protected\s+)?(def|fun) ([^\s\(\)\:]+).*$/)) { - this.newSymbol(matchData[4], vscode.SymbolKind.Function, this.containers[this.countEnds - 1], index, matchData) - if (matchData[1] === undefined) { - this.countEnds += 1 - } - } else if (matchData = element.match(/^\s*(private\s+)?(macro) ([^\s\(\)\:]+).*$/)) { - this.newSymbol(matchData[3], vscode.SymbolKind.Function, this.containers[this.countEnds - 1], index, matchData.index) - this.countEnds += 1 - } else if (matchData = element.match(/^\s*(abstract\s+)?(private\s+)?(class) ([A-Z][^\s\(\)]*).*$/)) { - this.newSymbol(matchData[4], vscode.SymbolKind.Class, this.containers[this.countEnds - 1], index, matchData.index) - this.setContainer(matchData[4]) - } else if (matchData = element.match(/^\s*(private\s+|protected\s+)?(class_)?(property|getter|setter)(!|\?)? ([^\s\(\)\:]+).*$/)) { - this.newSymbol(matchData[5], vscode.SymbolKind.Property, this.containers[this.countEnds - 1], index, matchData.index) - } else if (matchData = element.match(/^\s*(abstract\s+)?(private\s+)?(struct|record) ([A-Z][^\s\(\)]*).*$/)) { - this.newSymbol(matchData[4], vscode.SymbolKind.Struct, this.containers[this.countEnds - 1], index, matchData.index) - if (matchData[1] === undefined && matchData[3] !== "record") { - this.setContainer(matchData[4]) - } - } else if (matchData = element.match(/^\s*(private\s+)?(module) ([A-Z][^\s\(\)]*).*$/)) { - this.newSymbol(matchData[3], vscode.SymbolKind.Module, this.containers[this.countEnds - 1], index, matchData.index) - this.setContainer(matchData[3]) - } else if (matchData = element.match(/^\s*(private\s+)?(lib) ([A-Z][^\s\(\)\:]*).*$/)) { - this.newSymbol(matchData[3], vscode.SymbolKind.Module, this.containers[this.countEnds - 1], index, matchData.index) - this.countEnds += 1 - } else if (matchData = element.match(/^\s*(private\s+)?(enum|union) ([A-Z][^\s\(\)\:]*).*$/)) { - this.newSymbol(matchData[3], vscode.SymbolKind.Enum, this.containers[this.countEnds - 1], index, matchData.index) - this.countEnds += 1 - } else if (matchData = element.match(/^\s*(alias\s+|type\s+)?([A-Z][^\s\(\)\:]*)\s*=.*$/)) { - this.newSymbol(matchData[2], vscode.SymbolKind.Constant, this.containers[this.countEnds - 1], index, matchData.index) - this.incrementEndsCountIfKeywordIn(element) - } else if (matchData = element.match(/^\s*(\w[^\@\s\(\)\:]*)\s+:\s+.*$/)) { - this.newSymbol(matchData[1], vscode.SymbolKind.Variable, this.containers[this.countEnds - 1], index, matchData.index) - } else if (matchData = element.match(/^\s*(@\w[^\s\(\)\:]*)\s+:\s+.*$/)) { - this.newSymbol(matchData[1], vscode.SymbolKind.Variable, this.containers[this.countEnds - 1], index, matchData.index) - } else if (element.match(/^\s*end(\..*)?$/)) { - this.countEnds -= 1 - if (this.countEnds <= 0) { - this.containers = [] - } - } else if (element.match(/^\s*(if|unless|until|while)\s.*$/)) { - this.countEnds += 1 - } else if (element.match(/^.*=\s+(if|unless)\s.*$/)) { - this.countEnds += 1 - } else if (element.match(/^\s*(select|case|begin)$/)) { - this.countEnds += 1 - } else if (element.match(/^.*\sdo(\s\|.*)?$/)) { - this.countEnds += 1 - } else if (element.match(/^\s*case\s.*$/)) { - this.countEnds += 1 - } else if (element.match(/^.*\sbegin$/)) { - this.countEnds += 1 - } - } - } - return this.symbols - } -} \ No newline at end of file diff --git a/src/crystalTasks.ts b/src/crystalTasks.ts deleted file mode 100644 index 47abab8..0000000 --- a/src/crystalTasks.ts +++ /dev/null @@ -1,309 +0,0 @@ -import {ExtensionContext, Task, TaskDefinition, ShellExecution, ShellExecutionOptions, TextDocument, WorkspaceFolder, WorkspaceFoldersChangeEvent, TaskGroup, TaskPresentationOptions, TaskRevealKind, TaskPanelKind, Disposable, Uri, workspace, TaskProvider, tasks} from "vscode" -import * as path from 'path' -import * as fs from 'fs' -import * as YAML from 'yaml' - -// Copy from https://github.com/rust-lang/rls-vscode/blob/master/src/tasks.ts -export function registerCrystalTask(context: ExtensionContext): void { - workspace.onDidOpenTextDocument((doc) => DidOpenTextDocument(doc, context)) - workspace.textDocuments.forEach((doc) => DidOpenTextDocument(doc, context)) - workspace.onDidChangeWorkspaceFolders((e) => didChangeWorkspaceFolders(e, context)); -} - -export function DidOpenTextDocument(document: TextDocument, context: ExtensionContext): void { - if (document.languageId !== 'crystal') { - return - } - - const uri = document.uri - let folder = workspace.getWorkspaceFolder(uri) - if (!folder){ - return - } - - folder = getOuterMostWorkspaceFolder(folder); - if (!workspaces.has(folder.uri.toString())) { - const client = new CrystalTaskClient(folder) - workspaces.set(folder.uri.toString(), client) - client.start(context) - } -} - -export function getOuterMostWorkspaceFolder(folder: WorkspaceFolder): WorkspaceFolder { - const sorted = sortedWorkspaceFolders(); - for (const element of sorted) { - let uri = folder.uri.toString(); - if (uri.charAt(uri.length - 1) !== '/') { - uri = uri + '/'; - } - if (uri.startsWith(element)) { - return workspace.getWorkspaceFolder(Uri.parse(element)) || folder; - } - } - return folder; -} - -// This is an intermediate, lazy cache used by `getOuterMostWorkspaceFolder` -// and cleared when VSCode workspaces change. -let _sortedWorkspaceFolders: string[] | undefined; - -export function sortedWorkspaceFolders(): string[] { - if (!_sortedWorkspaceFolders && workspace.workspaceFolders) { - _sortedWorkspaceFolders = workspace.workspaceFolders.map(folder => { - let result = folder.uri.toString(); - if (result.charAt(result.length - 1) !== '/') { - result = result + '/'; - } - return result; - }).sort( - (a, b) => { - return a.length - b.length; - } - ); - } - return _sortedWorkspaceFolders || []; -} - -export function didChangeWorkspaceFolders(e: WorkspaceFoldersChangeEvent, context: ExtensionContext): void { - _sortedWorkspaceFolders = undefined; - - // If a VSCode workspace has been added, check to see if it is part of an existing one, and - // if not, and it is a Rust project (i.e., has a Cargo.toml), then create a new client. - for (let folder of e.added) { - folder = getOuterMostWorkspaceFolder(folder); - if (workspaces.has(folder.uri.toString())) { - continue; - } - - const client = new CrystalTaskClient(folder) - workspaces.set(folder.uri.toString(), client) - client.start(context) - } - - // If a workspace is removed which is a Crystal workspace, kill the client. - for (const folder of e.removed) { - const client = workspaces.get(folder.uri.toString()); - if (client) { - workspaces.delete(folder.uri.toString()); - client.stop() - } - } -} - -const workspaces: Map = new Map(); - -class CrystalTaskClient { - readonly folder: WorkspaceFolder; - disposables: Disposable[]; - - constructor(folder: WorkspaceFolder) { - this.folder = folder - this.disposables = [] - } - - async start(context: ExtensionContext) { - this.disposables.push(this.registerTaskProvider(TaskType.Crystal)) - this.disposables.push(this.registerTaskProvider(TaskType.Shards)) - } - - async stop() { - let promise: Thenable = Promise.resolve(void 0) - return promise.then(() => { - this.disposables.forEach(d => d.dispose()) - }) - } - - registerTaskProvider(taskType: TaskType) : Disposable { - let provider: TaskProvider = new CrystalTaskProvider(taskType, this.folder); - const disposable = tasks.registerTaskProvider(taskType, provider); - return disposable - } -} - -enum TaskType { - Crystal = 'crystal', - Shards = 'shards', -} - -class CrystalTaskProvider implements TaskProvider { - constructor(private _taskType: TaskType, private _workspaceFolder: WorkspaceFolder) { - } - - public provideTasks() { - return getCrystalTasks(this._taskType, this._workspaceFolder) - } - - public resolveTask(_task : Task): Task | undefined { - return undefined - } -} - -interface CrystalTaskDefinition extends TaskDefinition { - label: string - command: string - args?: Array - file?: string -} - -interface TaskConfigItem { - definition: CrystalTaskDefinition - problemMatcher: Array - group?: TaskGroup - presentationOptions?: TaskPresentationOptions -} - -function getCrystalTasks(taskType: TaskType, target: WorkspaceFolder): Task[] { - const taskList = createCrystalTaskConfigItem(taskType, target) - const list = taskList.map((def) => { - const task = createCrystalTask(def, target) - return task - }) - - return list -} - -function createCrystalTask({ definition, group, presentationOptions, problemMatcher }: TaskConfigItem, target: WorkspaceFolder): Task { - let taskBin = getCrystalCompiler(target) - let taskArgs: Array = (definition.args !== undefined) ? definition.args : [] - if (definition.file !== undefined) { - taskArgs.push(definition.file) - } - - let source = 'Crystal' - if (definition.type !== 'crystal') { - taskBin = getShardsPath(target) - source = 'Shards' - } - - const execCmd = `${taskBin} ${definition.command} ${taskArgs}` - const execOption: ShellExecutionOptions = { - cwd: target.uri.fsPath - } - const exec = new ShellExecution(execCmd, execOption) - let label = definition.label - if (definition.type == 'crystal' && definition.file !== undefined) { - label = `${label} - ${definition.file}` - } - - const task = new Task(definition, target, label, source, exec, problemMatcher) - if (group !== undefined) { - task.group = group - } - - if (presentationOptions !== undefined) { - task.presentationOptions = presentationOptions - } - - return task -} - -const CRYSTAL_TASKS: Array<{type: string, command: string, args?: Array, group: TaskGroup}> = [ - { - type: 'crystal', - command: 'run', - group: TaskGroup.Build - }, - { - type: 'crystal', - command: 'docs', - group: TaskGroup.Clean - }, - { - type: 'crystal', - command: 'tool format', - group: TaskGroup.Clean - }, - { - type: 'crystal', - command: 'spec', - group: TaskGroup.Test - }, - { - type: 'shards', - command: 'install', - group: TaskGroup.Build - }, - { - type: 'shards', - command: 'update', - group: TaskGroup.Build - }, - { - type: 'shards', - command: 'build', - args: [ - '--release' - ], - group: TaskGroup.Build - }, - { - type: 'shards', - command: 'prune', - group: TaskGroup.Clean - } -] - -function createCrystalTaskConfigItem(taskType: TaskType, target: WorkspaceFolder): Array { - const problemMatcher = [] - const presentationOptions: TaskPresentationOptions = { - reveal: TaskRevealKind.Always, - panel: TaskPanelKind.Dedicated, - } - const mainFile = getMainFile(target) - const tasks = CRYSTAL_TASKS.filter((task) => task.type === taskType).map((opt) => { - const def: CrystalTaskDefinition = { - label: opt.command, - type: opt.type, - command: opt.command, - args: opt.args, - } - - if (opt.type == 'crystal' && opt.group == TaskGroup.Build) { - def.file = mainFile - } - - const task = { - definition: def, - problemMatcher, - group: def.group, - presentationOptions - } - - return task - }) - - return tasks -} - -function getCrystalCompiler(folder: WorkspaceFolder): string { - return workspace.getConfiguration('crystal-lang', folder.uri).get('compiler', 'crystal') -} - -function getShardsPath(folder: WorkspaceFolder): string { - return workspace.getConfiguration('crystal-lang', folder.uri).get('shards', 'shards') -} - -function getMainFile(folder: WorkspaceFolder): string { - const shardFile = getShardFile() - if (fs.existsSync(shardFile)) { - const io = fs.readFileSync(shardFile, 'utf8') - const data = YAML.parse(io) - - if (data.targets !== undefined) { - const values = Object.keys(data.targets).map(key => data.targets[key]) - // NOTE: match first targets - if (values.length > 0) { - return values[0].main - } - } - } - - const defaultMainFile = workspace.getConfiguration('crystal-lang', folder.uri).get('mainFile', 'main.cr') - return defaultMainFile -} - -function getShardFile(): string { - const workspaceRoot = workspace.rootPath; - const shardFile = path.join(workspaceRoot, 'shard.yml') - return shardFile -} diff --git a/src/crystalUtils.ts b/src/crystalUtils.ts deleted file mode 100755 index 04514f0..0000000 --- a/src/crystalUtils.ts +++ /dev/null @@ -1,343 +0,0 @@ -import * as vscode from "vscode" -import { execSync, spawn, SpawnOptions } from "child_process" - -// ----------------------------- -// Private utilities (no export) -// ----------------------------- - -// Allow status bar messages -const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left) - -// VSCode config in settings.json -const config = vscode.workspace.getConfiguration("crystal-lang") - -// Allow to create enviroments variables -const CRENV = Object.create(process.env) - -// Root folder for current workspace -const ROOT = vscode.workspace.rootPath - -// Workspace checker for Bash on Windows -const WORKSPACE = (() => { - if (ROOT != undefined) { - let letter = ROOT.slice(0, 1).toLowerCase() - if (config["bashOnWindows"]) { - return '/mnt/' + letter + '/' + ROOT.slice(3).replace(/\\/g, '/') - } else { - return ROOT - } - } -})() || "" - -// Get and verify standard library path from Crystal env -const STDLIB = (() => { - const regex = /(.*)\n/; - try { - let output = execSync(`${config["compiler"]} env CRYSTAL_PATH`) - const match = regex.exec(output.toString()); - if (match && match.length === 2) { - return match[1] - } - return "lib" - } catch (ex) { - vscode.window.showWarningMessage("Crystal compiler not found. Some features can throw errors.") - console.error(ex) - return "lib" - } -})() - -// Set CRYSTAL_PATH for current workspace -CRENV.CRYSTAL_PATH = `${WORKSPACE}/lib:${STDLIB}` - -// Crystal keywords to ignore on hover -// https://github.com/crystal-lang/crystal/wiki/Crystal-for-Rubyists#available-keywords -const KEYWORDS = [ - "def", "if", "else", "elsif", "end", "true", "false", "class", "module", "include", - "extend", "while", "until", "nil", "do", "yield", "return", "unless", "next", "break", - "begin", "lib", "fun", "type", "struct", "union", "enum", "macro", "out", "require", - "case", "when", "in", "select", "then", "of", "rescue", "ensure", "is_a?", "alias", - "sizeof", "as", "as?", "typeof", "for", "in", "with", "self", "super", "private", "asm", - "nil?", "abstract", "pointerof", "protected", "uninitialized", "instance_sizeof" -] - -const spawnOptions : SpawnOptions = { - cwd: ROOT, - env: CRENV, - shell: "bash", - stdio: ["ignore", "pipe", "pipe"] -} - -/** - * Check main file in current workspace - */ -function mainFile(document) { - const config = vscode.workspace.getConfiguration("crystal-lang") - if (config["mainFile"]) { - return config["mainFile"].replace("${workspaceRoot}", WORKSPACE) - } - return document -} - -/** - * Check Linux path for Bash on Windows - */ -function tryLinuxPath(path: string) { - if (config["bashOnWindows"]) { - let letter = path.slice(0, 1).toLowerCase() - return `/mnt/${letter}/${path.slice(3).replace(/\\/g, '/')}` - } - return path -} - -// ----------------------------------- -// Public constants utilities (export) -// ----------------------------------- - -// Diagnostics provider -export const diagnosticCollection = vscode.languages.createDiagnosticCollection("crystal") - -/** - * Counter for Crystal processes - */ -export class Concurrent { - static counter = 0 - static limit() { - return config["processesLimit"] - } -} - -/** - * Check Windows path for Bash on Windows - */ -export function tryWindowsPath(path: string) { - if (config["bashOnWindows"]) { - let stdlib = STDLIB.split(":") - if (path.startsWith(stdlib[0]) || path.startsWith(stdlib[1])) { - console.error("ERROR: Windows can't access to Linux subsystem files yet.") - } - let letter = path[5] - let file = path.slice(6) - return `${letter}:${file}` - } - return path -} - -/** - * Check if word is a Crystal keyword - */ -export function isNotKeyword(word) { - return KEYWORDS.indexOf(word) < 0 -} - -/** - * Ensure that file is not inside lib folders - */ -export function isNotLib(file) { - let stdlib = STDLIB.split(":") - if (file.startsWith(stdlib[0]) || file.startsWith(stdlib[1]) || file.startsWith(`${ROOT}/lib`)) { - return false - } - return true -} - -/** - * Seach symbols for a crystal document - */ -export function getSymbols(uri): Thenable { - return vscode.commands.executeCommand("vscode.executeDocumentSymbolProvider", uri) - .then(symbols => symbols === undefined ? [] : symbols) -} - -/** - * Parse JSON response and create diagnostics - */ -export function searchProblems(response: string, uri: vscode.Uri) { - let diagnostics = [] - const config = vscode.workspace.getConfiguration("crystal-lang") - if (response.startsWith(`[{"file":"`)) { - try { - let results = JSON.parse(response) - let maxNumberOfProblems = config["maxNumberOfProblems"] - for (let [index, problem] of results.entries()) { - if (index >= maxNumberOfProblems) { - break - } - if (problem.line == null) { - problem.line = 1 - problem.column = 1 - } - let range = new vscode.Range(problem.line - 1, problem.column - 1, problem.line - 1, (problem.column + (problem.size || 0) - 1)) - let diagnostic = new vscode.Diagnostic(range, problem.message, vscode.DiagnosticSeverity.Error) - let file: vscode.Uri - if (problem.file.length > 0) { - if (!problem.file.endsWith(".cr")) { - file = vscode.Uri.file(vscode.workspace.rootPath + "/" + problem.file) - } else { - file = vscode.Uri.file(tryWindowsPath(problem.file)) - } - } else { - file = uri - } - diagnostics.push([file, [diagnostic]]) - } - } catch (err) { - console.error("ERROR: JSON.parse failed to parse Crystal output") - throw err - } - } else { - diagnosticCollection.clear() - } - if (config["problems"] != "none") { - diagnosticCollection.set(diagnostics) - } - return diagnostics -} - -/** - * Parse raw output from crystal tool format - response and create diagnostics - */ -export function searchProblemsFromRaw(response: string, uri: vscode.Uri) { - let diagnostics = [] - - const config = vscode.workspace.getConfiguration("crystal-lang") - - let responseData = response.match(/.* in .*?(\d+):\S* (.*)/) - - let parsedLine:number - - try { - parsedLine = parseInt(responseData[1]) - } catch (e) { - parsedLine = 0 - } - - let columnLocation = 1 // No way to get column from crystal tool format - - - if (parsedLine != 0) { - let problem = { - line: parsedLine, - column: columnLocation, - message: responseData[2] - } - - let range = new vscode.Range(problem.line - 1, problem.column - 1, problem.line - 1, problem.column - 1) - let diagnostic = new vscode.Diagnostic(range, problem.message, vscode.DiagnosticSeverity.Error) - diagnostics.push([uri, [diagnostic]]) - } - - if (diagnostics.length == 0) { - diagnosticCollection.clear() - } else if (config["problems"] != "none") { - diagnosticCollection.set(diagnostics) - } - - return diagnostics -} - -/** - * Execute Crystal tools context and implementations - */ -export function spawnTools(document, position, command, key) { - return new Promise(function (resolve, reject) { - let response = "" - const config = vscode.workspace.getConfiguration("crystal-lang") - if (Concurrent.counter < Concurrent.limit() && config[key]) { - let file = tryLinuxPath(document.fileName) - let scope = mainFile(file) - Concurrent.counter += 1 - statusBarItem.text = `${config["compiler"]} tool ${command} is working...` - statusBarItem.show() - let child = spawn(config["compiler"], [ - "tool", - command, - "-c", - `${file}:${position.line + 1}:${position.character + 1}`, - scope, - "--no-color", - "--error-trace", - "-f", - "json" - ], spawnOptions) - childOnStd(child, "data", (data) => { - response += data - }) - childOnStd(child, "end", () => { - searchProblems(response.toString(), document.uri) - Concurrent.counter -= 1 - statusBarItem.hide() - return resolve(response) - }) - childOnError(child) - } else if (config[key]) { - return resolve(`{"status":"blocked"}`) - } else { - return resolve("") - } - }) -} - -/** - * Execute Crystal compiler or parser - */ -export function spawnCompiler(document, build) { - const config = vscode.workspace.getConfiguration("crystal-lang") - let file = tryLinuxPath(document.fileName) - let scope = mainFile(file) - let response = "" - let args = (() => { - if (build) { - Concurrent.counter += 1 - statusBarItem.text = `${config["compiler"]} build --no-codegen is working...` - statusBarItem.show() - return [ - "build", - "--no-debug", - "--no-color", - "--no-codegen", - "--error-trace", - scope, - "-f", - "json" - ] - } - return [ - "tool", - "format", - "--check", - "--no-color", - scope - ] - })() - let child = spawn(config["compiler"], args, spawnOptions) - childOnStd(child, "data", (data) => { - response += data - }) - childOnStd(child, "end", () => { - if (build) { - searchProblems(response.toString(), document.uri) - Concurrent.counter -= 1 - statusBarItem.hide() - } else { - searchProblemsFromRaw(response.toString(), document.uri) - } - }) - childOnError(child) -} - -/** - * Helper method for using process stdout and stderr - */ -export function childOnStd(child, event, block) { - child.stdout.on(event, block) - child.stderr.on(event, block) -} - -/** - * Helper method for printing crystal plugin errors - */ -export function childOnError(child) { - child.on("error", (err) => { - vscode.window.showErrorMessage("Error executing Crystal plugin. " + err.message) - console.error(err.message) - }) -} diff --git a/src/definitions.ts b/src/definitions.ts new file mode 100644 index 0000000..27f20cd --- /dev/null +++ b/src/definitions.ts @@ -0,0 +1,118 @@ +import { existsSync } from 'fs'; +import * as path from 'path'; +import { + CancellationToken, + Definition, + DefinitionProvider, + DocumentSelector, + ExtensionContext, + languages, + Location, + LocationLink, + Position, + TextDocument, + Uri, + workspace, + WorkspaceFolder, +} from 'vscode'; +import { crystalOutputChannel, execAsync, findProblems, getCompilerPath, getCursorPath, getShardMainPath, getWorkspaceFolder, shellEscape } from './tools'; + +class CrystalDefinitionProvider implements DefinitionProvider { + async provideDefinition( + document: TextDocument, + position: Position, + token: CancellationToken + ): Promise { + const line = document.lineAt(position.line); + const matches = /^require\s+"(.+)"\s*$/.exec(line.text); + + if (matches?.length > 1) { + let text = matches[1]; + if (text.includes('*')) return []; + const dir = path.dirname(document.fileName); + crystalOutputChannel.appendLine(`[Implementations] identified: ${text}`); + + if (/^\.{1,2}\/\w+/.test(text)) { + if (!text.endsWith('.cr')) text += '.cr'; + const loc = path.join(dir, text); + if (!existsSync(loc)) return []; + + return new Location(Uri.file(loc), new Position(0, 0)); + } + + // TODO: implement shard lookup + return []; + } + + try { + crystalOutputChannel.appendLine('[Implementations] getting implementations...'); + const res = await spawnImplTool(document, position); + if (res.status !== 'ok') { + crystalOutputChannel.appendLine(`[Implementations] failed: ${res.message}`); + return []; + } + + const links: Location[] = []; + for (let impl of res.implementations!) { + links.push( + new Location( + Uri.file(impl.filename), + new Position(impl.line - 1, impl.column - 1) + ) + ); + } + + return links; + } catch (err) { + if (err.stderr) { + findProblems(err.stderr, document.uri) + crystalOutputChannel.appendLine(`[Implementations] failed: ${err.stderr}`) + } else if (err.message) { + crystalOutputChannel.appendLine(`[Implementations] failed: ${err.message}`); + } else { + crystalOutputChannel.appendLine(`[Implementations] failed: ${JSON.stringify(err)}`); + } + return []; + } + } +} + +export function registerDefinitions( + selector: DocumentSelector, + context: ExtensionContext +): void { + context.subscriptions.push( + languages.registerDefinitionProvider( + selector, + new CrystalDefinitionProvider() + ) + ); +} + +interface ImplResponse { + status: string; + message: string; + implementations?: { + line: number; + column: number; + filename: string; + }[]; +} + +async function spawnImplTool( + document: TextDocument, + position: Position +): Promise { + const compiler = await getCompilerPath(); + const config = workspace.getConfiguration('crystal-lang'); + const cursor = getCursorPath(document, position); + const main = await getShardMainPath(document); + const cmd = `${shellEscape(compiler)} tool implementations -c ${shellEscape(cursor)} ${shellEscape(main)} -f json --no-color ${config.get("flags")}` + const folder: WorkspaceFolder = getWorkspaceFolder(document.uri) + + crystalOutputChannel.appendLine(`[Implementations] (${folder.name}) $ ${cmd}`); + + return JSON.parse( + await execAsync(cmd, folder.uri.fsPath) + ); +} diff --git a/src/definitions/globals.ts b/src/definitions/globals.ts new file mode 100644 index 0000000..4c48d04 --- /dev/null +++ b/src/definitions/globals.ts @@ -0,0 +1,121 @@ +const CLASSES = [ + ['ArgumentError', 'class ArgumentError'], + ['Array', 'class Array(T)'], + ['Box', 'class Box(T)'], + ['Channel', 'abstract class Channel(T)'], + ['CSV', 'class CSV'], + ['Deque', 'class Deque(T)'], + ['Dir', 'class Dir'], + ['DivisionByZero', 'class DivisionByZero'], + ['Errno', 'class Errno'], + ['Exception', 'class Exception'], + ['Fiber', 'class Fiber'], + ['File', 'class File'], + ['Hash', 'class Hash(K, V)'], + ['IndexError', 'class IndexError'], + ['INI', 'class INI'], + ['IPSocket', 'class IPSocket'], + ['KeyError', 'class KeyError'], + ['Log', 'class Log'], + ['Mutex', 'class Mutex'], + ['Object', 'abstract class Object'], + ['OptionParser', 'class OptionParser'], + ['PrettyPrint', 'class PrettyPrint'], + ['Process', 'class Process'], + ['Reference', 'class Reference'], + ['Regex', 'class Regex'], + ['Socket', 'class Socket'], + ['String', 'class String'], + ['StringPool', 'class StringPool'], + ['StringScanner', 'class StringScanner'], + ['TypeCastError', 'class TypeCastError'], + ['URI', 'class URI'], + ['Weakref', 'class WeakRef(T)'], +]; + +const MODULES = [ + ['Base64', 'module Base64'], + ['Benchmark', 'module Benchmark'], + ['Colorize', 'module Colorize'], + ['Comparable', 'module Comparable(T)'], + ['Crypto', 'module Crypto'], + ['Crystal', 'module Crystal'], + ['Digest', 'module Digest'], + ['ECR', 'module ECR'], + ['Enumerable', 'module Enumerable(T)'], + ['FileUtils', 'module FileUtils'], + ['GC', 'module GC'], + ['HTML', 'module HTML'], + ['HTTP', 'module HTTP'], + ['Indexable', 'module Indexable(T)'], + ['IO', 'module IO'], + ['Iterable', 'module Iterable(T)'], + ['Iterator', 'module Iterator(T)'], + ['JSON', 'module JSON'], + ['Levenshtein', 'module Levenshtein'], + ['LLVM', 'module LLVM'], + ['Math', 'module Math'], + ['OAuth', 'module OAuth'], + ['OAuth2', 'module OAuth2'], + ['OpenSSL', 'module OpenSSL'], + ['Random', 'module Random'], + ['Spec', 'module Spec'], + ['System', 'module System'], + ['Termios', 'module Termios'], + ['Unicode', 'module Unicode'], + ['XML', 'module XML'], + ['YAML', 'module YAML'], +]; + +const OTHERS = [ + ['Bytes', 'alias Bytes'], + ['Signal', 'enum Signal'], +]; + +const STRUCTS = [ + ['Atomic', 'struct Atomic(T)'], + ['BigFloat', 'struct BigFloat'], + ['BigInt', 'struct BigInt'], + ['BigRational', 'struct BigRational'], + ['BitArray', 'struct BitArray'], + ['Bool', 'struct Bool'], + ['Char', 'struct Char'], + ['Complex', 'struct Complex'], + ['Enum', 'abstract struct Enum'], + ['Float', 'abstract struct Float'], + ['Float32', 'struct Float32'], + ['Float64', 'struct Float64'], + ['Int', 'abstract struct Int'], + ['Int8', 'struct Int8'], + ['Int16', 'struct Int16'], + ['Int32', 'struct Int32'], + ['Int64', 'struct Int64'], + ['Int128', 'struct Int128'], + ['NamedTuple', 'struct NamedTuple(**T)'], + ['Nil', 'struct Nil'], + ['Number', 'abstract struct Number'], + ['Pointer', 'struct Pointer(T)'], + ['Proc', 'struct Proc(*T, R)'], + ['Range', 'struct Range(B, E)'], + ['Set', 'struct Set(T)'], + ['Slice', 'struct Slice(T)'], + ['StaticArray', 'struct StaticArray(T, N)'], + ['Symbol', 'struct Symbol'], + ['Time', 'struct Time'], + ['Tuple', 'struct Tuple(*T)'], + ['UInt', 'struct UInt'], + ['UInt8', 'struct UInt8'], + ['UInt16', 'struct UInt16'], + ['UInt32', 'struct UInt32'], + ['UInt64', 'struct UInt64'], + ['UInt128', 'struct UInt128'], + ['Union', 'struct Union(*T)'], + ['Value', 'abstract struct Value'], +]; + +export default { + CLASSES, + MODULES, + OTHERS, + STRUCTS, +}; diff --git a/src/definitions/index.ts b/src/definitions/index.ts new file mode 100644 index 0000000..9013469 --- /dev/null +++ b/src/definitions/index.ts @@ -0,0 +1,62 @@ +export * from './globals'; +export * from './methods'; + +export const KEYWORDS = [ + 'def', + 'if', + 'else', + 'elsif', + 'end', + 'true', + 'false', + 'class', + 'module', + 'include', + 'extend', + 'while', + 'until', + 'nil', + 'do', + 'yield', + 'return', + 'unless', + 'next', + 'break', + 'begin', + 'lib', + 'fun', + 'type', + 'struct', + 'union', + 'enum', + 'macro', + 'out', + 'require', + 'case', + 'when', + 'in', + 'select', + 'then', + 'of', + 'rescue', + 'ensure', + 'is_a?', + 'alias', + 'sizeof', + 'as', + 'as?', + 'typeof', + 'for', + 'in', + 'with', + 'self', + 'super', + 'private', + 'asm', + 'nil?', + 'abstract', + 'pointerof', + 'protected', + 'uninitialized', + 'instance_sizeof', +]; diff --git a/src/definitions/methods/index.ts b/src/definitions/methods/index.ts new file mode 100644 index 0000000..d1120a8 --- /dev/null +++ b/src/definitions/methods/index.ts @@ -0,0 +1,224 @@ +const PSEUDO = [ + [ + 'is_a?', + 'is_a?', + "The pseudo-method is_a? determines whether an expression's runtime type inherits or includes another type.", + ], + [ + 'nil?', + 'nil?', + "The pseudo-method nil? determines whether an expression's runtime is Nil.", + ], + [ + 'responds_to?', + 'responds_to?', + 'The pseudo-method responds_to? determines whether a type has a method with the given name.', + ], + ['as', 'as', 'The as pseudo-method restricts the types of an expression.'], + [ + 'as?', + 'as?', + "The as? pseudo-method is similar to as, except that it returns nil instead of raising an exception when the type doesn't match. It also can't be used to cast between pointer types and other types.", + ], +]; + +const OBJECT = [ + ['==', '==(other)', 'Returns `true` if this object is equal to *other*.'], + ['!=', '!=(other)', 'Returns `true` if this object is not equal to *other*.'], + ['!~', '!~(other)', 'Shortcut to `!(self =~ other)`.'], + ['===', '===(other)', 'Case equality.'], + ['=~', '=~(other)', 'Pattern match.'], + [ + 'hash', + 'hash(other)', + "Appends this object's value to *hasher*, and returns the modified *hasher*.", + ], + ['hash', 'hash', 'Generates an `UInt64` hash value for this object.'], + ['to_s', 'to_s : String', 'Returns a string representation of this object.'], + [ + 'to_s', + 'to_s(io : IO) : Nil', + 'Appends a `String` representation of this object to the given `IO` object.', + ], + [ + 'inspect', + 'inspect : String', + 'Returns a `String` representation of this object suitable to be embedded inside other expressions, sometimes providing more information about this object.', + ], + [ + 'inspect', + 'inspect(io : IO) : Nil', + 'Appends a string representation of this object to the given `IO` object.', + ], + [ + 'pretty_print', + 'pretty_print(pp : PrettyPrint) : Nil', + 'Pretty prints `self` into the given printer.', + ], + [ + 'pretty_inspect', + 'pretty_inspect(width = 79, newline = "\n", indent = 0) : String', + 'Returns a pretty printed version of `self`.', + ], + ['tap', 'tap', 'Yields `self` to the block, and then returns `self`.'], + [ + 'try', + 'try', + "Yields `self`. `Nil` overrides this method and doesn't yield.", + ], + [ + 'in?', + 'in?(collection : Object) : Bool', + 'Returns `true` if `self` is included in the *collection* argument.', + ], + [ + 'in?', + 'in?(*values : Object) : Bool', + 'Returns `true` if `self` is included in the *collection* argument.', + ], + ['not_nil!', 'not_nil!', 'Returns `self`.'], + ['itself', 'itself', 'Returns `self`.'], + ['dup', 'dup', 'Returns a shallow copy (“duplicate”) of this object.'], + [ + 'unsafe_as', + 'unsafe_as(type : T.class) forall T', + 'Unsafely reinterprets the bytes of an object as being of another `type`.', + ], +]; + +const TOP_LEVEL = [ + [ + '`', + '`(command) : String', + 'Returns the standard output of executing command in a subshell.', + ], + [ + 'abort', + 'abort(message, status = 1) : NoReturn', + 'Terminates execution immediately, printing message to STDERR and then calling exit(status).', + ], + [ + 'at_exit', + 'at_exit(&handler : Int32 -> ) : Nil', + 'Registers the given Proc for execution when the program exits.', + ], + [ + 'delay', + 'delay(delay, &block : -> _)', + 'Spawns a Fiber to compute &block in the background after delay has elapsed.', + ], + [ + 'exit', + 'exit(status = 0) : NoReturn', + 'Terminates execution immediately, returning the given status code to the invoking environment.', + ], + ['fork', 'fork', 'See also: Process.fork'], + ['fork', 'fork(&block)', 'See also: Process.fork'], + [ + 'future', + 'future(&exp : -> _)', + 'Spawns a Fiber to compute &block in the background.', + ], + ['gets', 'gets(*args, **options)', 'Reads a line from STDIN.'], + [ + 'lazy', + 'lazy(&block : -> _)', + 'Conditionally spawns a Fiber to run &block in the background.', + ], + [ + 'loop', + 'loop(&block)', + 'Repeatedly executes the block, passing an incremental Int32 that starts with 0.', + ], + [ + 'p', + 'p(*objects)', + 'Pretty prints each object in objects to STDOUT, followed by a newline.', + ], + [ + 'p', + 'p', + 'Pretty prints each object in objects to STDOUT, followed by a newline.', + ], + ['p', 'p(object)', 'Pretty prints object to STDOUT followed by a newline.'], + [ + 'print', + 'print(*objects : _) : Nil', + 'Prints objects to STDOUT and then invokes STDOUT.flush.', + ], + [ + 'printf', + 'printf(format_string, args : Array | Tuple) : Nil', + 'Prints a formatted string to STDOUT.', + ], + [ + 'printf', + 'printf(format_string, *args) : Nil', + 'Prints a formatted string to STDOUT.', + ], + [ + 'puts', + 'puts(*objects) : Nil', + 'Prints objects to STDOUT, each followed by a newline.', + ], + ['rand', 'rand(x)', 'See Random#rand(x).'], + ['rand', 'rand', 'See Random#rand.'], + ['read_line', 'read_line(*args, **options)', 'Reads a line from STDIN.'], + ['sleep', 'sleep', 'Blocks the current fiber forever.'], + [ + 'sleep', + 'sleep(time : Time::Span)', + 'Blocks the current Fiber for the specified time span.', + ], + [ + 'sleep', + 'sleep(seconds : Number)', + 'Blocks the current fiber for the specified number of seconds.', + ], + ['spawn', 'spawn(*, name : String? = nil, &block)', 'Spawns a new fiber.'], + [ + 'sprintf', + 'sprintf(format_string, *args) : String', + 'Returns a formatted string.', + ], + [ + 'sprintf', + 'sprintf(format_string, args : Array | Tuple) : String', + 'Returns a formatted string.', + ], + [ + 'system', + 'system(command : String, args = nil) : Bool', + 'Executes the given command in a subshell.', + ], + [ + 'pp', + 'pp(*exps)', + 'Prints a series of expressions together with their values.', + ], + [ + 'record', + 'record(name, *properties)', + 'Defines a Struct with the given name and properties.', + ], + [ + 'spawn', + 'spawn(call, *, name = nil)', + "Spawns a fiber by first creating a Proc, passing the call's expressions to it, and letting the Proc finally invoke the call.", + ], + ['caller', 'caller', ''], + ['raise', 'raise(ex : Exception) : NoReturn', ''], + ['raise', 'raise(message : String) : NoReturn', ''], + ['with_color', 'with_color(color : Symbol)', ''], + ['with_color', 'with_color', ''], + ['assert_responds_to', 'assert_responds_to(var, method)', ''], + ['debugger', 'debugger', ''], + ['parallel', 'parallel(*jobs)', ''], + ['redefine_main', 'redefine_main(name = main)', ''], +]; + +export default { + PSEUDO, + OBJECT, + TOP_LEVEL, +}; diff --git a/src/extension.ts b/src/extension.ts new file mode 100644 index 0000000..c0b0b11 --- /dev/null +++ b/src/extension.ts @@ -0,0 +1,97 @@ +import { + ExtensionContext, + IndentAction, + LanguageConfiguration, + languages, + workspace, +} from 'vscode'; +import { registerCompletion } from './completion'; +import { registerFormatter } from './format'; +import { registerHover } from './hover'; +import { registerDefinitions } from './definitions'; +import { registerSymbols } from './symbols'; +import { CrystalTestingProvider } from './spec'; +import { registerMacroExpansion } from './macro'; +import { crystalOutputChannel } from './tools'; +import { registerTasks } from './tasks'; +import { existsSync } from 'fs'; +import { LanguageClient, LanguageClientOptions, DocumentSelector, MessageTransports, ServerOptions } from "vscode-languageclient/node" +import { registerProblems } from './problems'; + +const selector: DocumentSelector = [ + { language: 'crystal', scheme: 'file' }, + { language: 'ecr', scheme: 'file' } +]; + +export const crystalConfiguration = { + comments: { lineComment: '#' }, + indentationRules: { + increaseIndentPattern: + /^\s*((begin|(private\s+abstract|private|abstract)\s+(module|class|struct|enum)|class|struct|(private|protected)\s+def|def|fun|macro|else|elsif|ensure|for|if|module|enum|rescue|unless|until|when|while|case)|([^#]*\sdo\b)|([^#]*=\s*(case|if|unless)))\b([^#\{;]|("|'|\/).*\4)*(#.*)?$/, + decreaseIndentPattern: + /^\s*([}\]]([,)]?\s*(#|$)|\.[a-zA-Z_]\w*\b)|(end|rescue|ensure|else|elsif|when|(?:case[\s\S\n]+)in)\b)/, + }, + onEnterRules: [ + { + beforeText: /^\s*#(?!{).*$/, + action: { + appendText: '# ', + indentAction: IndentAction.None, + }, + }, + ], + wordPattern: + /(-?(?:0(?:b|o|x))?\d+(?:\.\d+)?(?:_?[iuf]\d+)?)|@{0,2}(:?[A-Za-z][^-`~@#%^&()=+[{}|;:'",<>\/.*\]\s\\!?]*[!?]?)/, +}; + +let lsp_client: LanguageClient + +export async function activate(context: ExtensionContext): Promise { + const config = workspace.getConfiguration("crystal-lang"); + const lsp = config["server"] + + // Specs enabled regardless of LSP support + if (config["spec-explorer"]) new CrystalTestingProvider(); + + // Language configuration independent of LSP + context.subscriptions.push( + languages.setLanguageConfiguration('crystal', crystalConfiguration) + ); + + if (existsSync(lsp)) { + crystalOutputChannel.appendLine(`[Crystal] loading lsp ${lsp}`) + + let serverOptions: ServerOptions = { command: lsp, args: [] } + let clientOptions: LanguageClientOptions = { + documentSelector: selector, + synchronize: { + configurationSection: "crystal-lang", + fileEvents: workspace.createFileSystemWatcher("**/*.cr") + }, + outputChannel: crystalOutputChannel + } + let lsp_client = new LanguageClient("Crystal Language", serverOptions, clientOptions) + lsp_client.start() + + return; + } else { + registerCompletion(selector, context); + registerFormatter(selector, context); + registerSymbols(selector, context); + registerMacroExpansion(); + registerTasks(context); + if (config["hover"]) registerHover(selector, context); + if (config["definitions"]) registerDefinitions(selector, context); + if (config["problems"]) registerProblems(); + + crystalOutputChannel.appendLine('[Crystal] extension loaded'); + } +} + +export function deactivate() { + if (lsp_client) { + return lsp_client.stop() + } + + return; +} diff --git a/src/format.ts b/src/format.ts new file mode 100644 index 0000000..871d527 --- /dev/null +++ b/src/format.ts @@ -0,0 +1,98 @@ +import { + CancellationToken, + DocumentFormattingEditProvider, + DocumentSelector, + ExtensionContext, + FormattingOptions, + languages, + Range, + TextDocument, + TextEdit, +} from 'vscode'; +import { crystalOutputChannel, findProblemsRaw, getCompilerPath, setStatusBar } from './tools'; +import { spawn } from 'child_process'; + +export function getFormatRange(document: TextDocument): Range { + return new Range( + 0, + 0, + document.lineCount, + document.lineAt(document.lineCount - 1).text.length + ); +} + +class CrystalFormattingEditProvider implements DocumentFormattingEditProvider { + async provideDocumentFormattingEdits( + document: TextDocument, + options: FormattingOptions, + token: CancellationToken + ): Promise { + if (document.fileName.endsWith(".ecr")) return; + + const dispose = setStatusBar('running format tool...'); + try { + crystalOutputChannel.appendLine('[Format] formatting...'); + const format = await spawnFormatTool(document); + if (!format.length) return; + + return [TextEdit.replace(getFormatRange(document), format)]; + } catch (err) { + if (!err) return; + + crystalOutputChannel.appendLine(`[Format] failed: ${err}`); + return []; + } finally { + dispose(); + } + } +} + +export function registerFormatter( + selector: DocumentSelector, + context: ExtensionContext +): void { + context.subscriptions.push( + languages.registerDocumentFormattingEditProvider( + selector, + new CrystalFormattingEditProvider() + ) + ); +} + +async function spawnFormatTool(document: TextDocument): Promise { + const compiler = await getCompilerPath(); + + return await new Promise((res, rej) => { + const child = spawn( + compiler, + ['tool', 'format', '--no-color', '-'], + { shell: process.platform == "win32" } + ); + + child.stdin.write(document.getText()); + child.stdin.end(); + + const out: string[] = []; + const err: string[] = []; + + child.stdout + .setEncoding('utf-8') + .on('data', d => out.push(d)); + + child.stderr + .setEncoding('utf-8') + .on('data', d => err.push(d)); + + child.on('close', () => { + if (err.length > 0) { + const err_resp = err.join('') + findProblemsRaw(err_resp, document.uri) + rej(err_resp); + return; + } + const out_resp = out.join('') + findProblemsRaw(out_resp, document.uri) + res(out_resp); + }) + }); +} diff --git a/src/hover.ts b/src/hover.ts new file mode 100644 index 0000000..ad39ca5 --- /dev/null +++ b/src/hover.ts @@ -0,0 +1,285 @@ +import { existsSync, readdirSync } from 'fs'; +import * as path from 'path'; +import { + CancellationToken, + DocumentSelector, + ExtensionContext, + Hover, + HoverProvider, + languages, + MarkdownString, + Position, + TextDocument, + TextLine, + workspace, +} from 'vscode'; +import { KEYWORDS } from './definitions/index'; +import { + crystalOutputChannel, + findProblems, + getCrystalLibPath, + getMainForShard, + setStatusBar, + compiler_mutex, + getWorkspaceFolder, + getCompilerPath, + execAsync, + getCursorPath, + getShardMainPath, + shellEscape +} from './tools'; +import { crystalConfiguration } from './extension'; + +class CrystalHoverProvider implements HoverProvider { + previousDoc = undefined; + previousText = ""; + + async provideHover( + document: TextDocument, + position: Position, + token: CancellationToken + ): Promise { + if (compiler_mutex.isLocked()) return; + + const line = document.lineAt(position.line); + if (!line.text || /^#(?!{).+/.test(line.text)) return; + if (/require\s+"(\.{1,2}\/[\w\*\/]+)"/.test(line.text)) + return await this.provideLocalRequireHover(document, line); + + if (/require\s+"([\w\/v-]+)"/.test(line.text)) + return await this.provideShardRequireHover(document, line); + + const pattern = crystalConfiguration.wordPattern; + const wordRange = document.getWordRangeAtPosition(position, pattern) + const text = document.getText(wordRange); + if (KEYWORDS.includes(text)) return; // TODO: potential custom keyword highlighting/info support? Rust?? + + const dispose = setStatusBar('running context tool...'); + + return await compiler_mutex.acquire() + .then(async (release) => { + return this.provideHoverInternal(document, position, line, text) + .catch((err) => { + if (err && err.stderr) { + findProblems(err.stderr, document.uri) + crystalOutputChannel.appendLine(`[Hover] error: ${JSON.stringify(err.stderr)}`); + } else if (err && err.message) { + crystalOutputChannel.appendLine(`[Hover] error: ${err.message}`); + } else { + crystalOutputChannel.appendLine(`[Hover] error: ${JSON.stringify(err)}`) + } + return undefined; + }) + .finally(() => { + release() + }) + }) + .finally(() => { + dispose() + }); + } + + private async provideHoverInternal(document: TextDocument, position: Position, line: TextLine, text: string): Promise { + return new Promise(async (resolve, reject) => { + crystalOutputChannel.appendLine('[Hover] getting context...'); + const res = await spawnContextTool(document, position) + + if (res === undefined) { + reject(res); + return; + } + + if (res.status !== 'ok') { + crystalOutputChannel.appendLine(`[Hover] failed: ${res.message}`); + reject(res); + return; + } + + // TODO: Filter/select based on text around cursor position + // will provide multiple contexts / all contexts on line + crystalOutputChannel.appendLine(`[Hover] context: ${res.message}`) + crystalOutputChannel.appendLine(`[Hover] context: ${JSON.stringify(res)}`) + + var ctx_key = line.text; + var ctx = res.contexts!.find(c => c[ctx_key]); + var ctx_value: string; + + if (ctx !== undefined) { + ctx_value = ctx[ctx_key]; + } else { + ctx_key = text + ctx = res.contexts!.find(c => c[ctx_key]); + } + + if (ctx !== undefined) { + ctx_value = ctx[ctx_key]; + } else { + ctx_key = text + for (var i = 0; i < res.contexts!.length; i += 1) { + const context: Record = res.contexts![i] + const key = Object.keys(context).find(key => key.includes(ctx_key)) + + if (key) { + ctx = context as Record + ctx_value = ctx[key] + break + } + } + } + + if (ctx === undefined || ctx_key.includes("\n")) { + resolve(undefined); + return; + } + + crystalOutputChannel.appendLine(`[Hover] context: ${ctx_key}: ${JSON.stringify(ctx_value)}`); + + const md = new MarkdownString().appendCodeblock( + ctx_key + " : " + ctx_value, + 'crystal' + ); + resolve(new Hover(md)); + }) + } + + // TODO: implement symbol check + // private provideSymbolContext() + + private async provideLocalRequireHover( + document: TextDocument, + line: TextLine + ): Promise { + const folder = getWorkspaceFolder(document.uri); + + const md = new MarkdownString(); + let match = /"(\.{1,2}\/[\w\*\/]+)"/.exec(line.text)[1]; + crystalOutputChannel.appendLine('[Hover] identifying local require'); + + if (match.includes('*')) { + const glob_path = path.join(path.dirname(document.uri.fsPath).replace(folder.uri.fsPath + path.sep, ""), match) + crystalOutputChannel.appendLine(`[Hover] globbing: ${glob_path}`); + const files = await workspace.findFiles(glob_path); + if (!files.length) return; + const lines: string[] = []; + + for (let file of files.slice(0, 10)) { + lines.push(`- [${path.relative(folder.uri.fsPath, file.fsPath)}](file:${path.sep}${path.sep}${file.fsPath})`); + } + lines.sort(); + + const extra = files.length - 10; + if (extra > 0) lines.push(`\n...and ${extra} more`); + md.appendText(`Resolved ${files.length} sources:\n`) + .appendMarkdown(lines.join('\n')); + } else { + if (!match.endsWith('.cr')) match += '.cr'; + const dir = path.dirname(document.fileName); + const src = path.resolve(dir, match); + if (!existsSync(src)) return; + crystalOutputChannel.appendLine(`[Hover] resolved: ${src}`); + const relative = path.relative(folder.uri.fsPath, src) + md.appendCodeblock(`require "${relative}"`, 'crystal') + .appendMarkdown(`[Go to source](file:${path.sep}${path.sep}${src})`); + } + + return new Hover(md, line.range); + } + + private async provideShardRequireHover( + document: TextDocument, + line: TextLine + ): Promise { + const folder = getWorkspaceFolder(document.uri); + const md = new MarkdownString(); + const match = /"([\w\/v-]+)"/.exec(line.text)[1]; + crystalOutputChannel.appendLine('[Hover] identifying shard/lib require'); + + const main = getMainForShard(document, match); + if (main) { + crystalOutputChannel.appendLine(`[Hover] resolved: ${main}`); + + md.appendCodeblock(`require "${path.relative(folder.uri.fsPath, main)}"`, 'crystal').appendMarkdown( + `[Go to source](file:${path.sep}${path.sep}${main})` + ); + } else { + try { + crystalOutputChannel.appendLine('[Hover] getting crystal path...'); + const libpath = await getCrystalLibPath(); + let fp = path.join(libpath, match); + if (!existsSync(fp)) { + fp += '.cr'; + if (!existsSync(fp)) return; + } + crystalOutputChannel.appendLine(`[Hover] resolved: ${fp}`); + + if (!fp.endsWith('.cr')) { + if (existsSync(fp + '.cr')) { + fp += '.cr'; + } else { + // TODO: levenshtein the fuck out of this + fp = path.join(fp, readdirSync(fp)[0]); + crystalOutputChannel.appendLine(`[Hover] expanded to: ${fp}`); + } + } + + md.appendCodeblock(`require "${match}"`, 'crystal').appendMarkdown( + `[Go to source](file:${path.sep}${path.sep}${fp})` + ); + } catch (err) { + crystalOutputChannel.appendLine(`[Hover] failed: ${JSON.parse(err.stderr).message}`); + return; + } + } + + return new Hover(md, line.range); + } +} + +export function registerHover( + selector: DocumentSelector, + context: ExtensionContext +): void { + context.subscriptions.push( + languages.registerHoverProvider(selector, new CrystalHoverProvider()) + ); +} + + +interface ContextResponse { + status: string; + message: string; + contexts?: Record[]; +} + +interface ContextError { + file: string; + line: number; + column: number; + message: string; +} + +async function spawnContextTool( + document: TextDocument, + position: Position, + dry_run: boolean = false +): Promise { + const compiler = await getCompilerPath(); + const cursor = getCursorPath(document, position); + const config = workspace.getConfiguration('crystal-lang'); + // Spec files shouldn't have main set to something in src/ + // but are instead their own main files + const main = await getShardMainPath(document); + const cmd = `${shellEscape(compiler)} tool context -c ${shellEscape(cursor)} ${shellEscape(main)} -f json --no-color ${config.get("flags")}` + const folder = getWorkspaceFolder(document.uri) + + crystalOutputChannel.appendLine(`[Context] (${folder.name}) $ ${cmd}`); + + return await execAsync(cmd, folder.uri.fsPath) + .then((response) => { + findProblems(response, document.uri); + return JSON.parse(response); + }).catch((err) => { + findProblems(err.stderr, document.uri); + crystalOutputChannel.appendLine(`[Context] error: ${err.stderr}`) + }) +} diff --git a/src/macro.ts b/src/macro.ts new file mode 100644 index 0000000..f32987c --- /dev/null +++ b/src/macro.ts @@ -0,0 +1,52 @@ +import { Position, TextDocument, commands, window, workspace } from 'vscode'; +import { crystalOutputChannel, execAsync, findProblems, getCompilerPath, getCursorPath, getShardMainPath, getWorkspaceFolder, shellEscape } from './tools'; + +export const macroOutputChannel = window.createOutputChannel("Crystal Macro", "markdown") + +export function registerMacroExpansion() { + commands.registerCommand('crystal-lang.showMacroExpansion', async function () { + const activeEditor = window.activeTextEditor; + const document = activeEditor.document; + const position = activeEditor.selection.active; + + const response = await spawnMacroExpandTool(document, position) + .then((resp) => { + if (resp) { + return resp + } else { + return undefined + } + }) + + if (response) { + macroOutputChannel.appendLine("```crystal\n" + response + "```\n") + } else { + macroOutputChannel.appendLine("# No macro expansion found") + } + macroOutputChannel.show() + }) +} + +export async function spawnMacroExpandTool(document: TextDocument, position: Position): Promise { + const compiler = await getCompilerPath(); + const main = await getShardMainPath(document); + const cursor = getCursorPath(document, position); + const folder = getWorkspaceFolder(document.uri); + const config = workspace.getConfiguration('crystal-lang'); + + const cmd = `${shellEscape(compiler)} tool expand ${shellEscape(main)} --cursor ${shellEscape(cursor)} ${config.get("flags")}` + + crystalOutputChannel.appendLine(`[Macro Expansion] (${folder.name}) $ ` + cmd) + return await execAsync(cmd, folder.uri.fsPath) + .then((response) => { + return response; + }) + .catch(async (err) => { + const new_cmd = cmd + ' -f json' + await execAsync(new_cmd, folder.uri.fsPath) + .catch((err) => { + findProblems(err.stderr, document.uri) + crystalOutputChannel.appendLine(`[Macro Expansion] Error: ${err.message}`) + }) + }); +} diff --git a/src/problems.ts b/src/problems.ts new file mode 100644 index 0000000..5033059 --- /dev/null +++ b/src/problems.ts @@ -0,0 +1,60 @@ +import { TextDocument, workspace } from "vscode"; +import { setStatusBar, compiler_mutex, crystalOutputChannel, diagnosticCollection, execAsync, findProblems, getCompilerPath, getShardMainPath, getWorkspaceFolder, shellEscape } from "./tools"; + +export function registerProblems(): void { + workspace.onDidSaveTextDocument((e) => { + if (e.uri && e.uri.scheme === "file" && (e.fileName.endsWith(".cr") || e.fileName.endsWith(".ecr"))) { + if (compiler_mutex.isLocked()) return; + + const dispose = setStatusBar('finding problems...'); + + compiler_mutex.acquire() + .then((release) => { + spawnProblemsTool(e) + .catch((err) => { + crystalOutputChannel.appendLine(`[Problems] Error: ${JSON.stringify(err)}`) + }) + .finally(() => { + release() + }) + }) + .finally(() => { + dispose() + }) + } + }) + + return; +} + +async function spawnProblemsTool(document: TextDocument): Promise { + const compiler = await getCompilerPath(); + const main = await getShardMainPath(document); + const folder = getWorkspaceFolder(document.uri).uri.fsPath; + const config = workspace.getConfiguration('crystal-lang'); + + // If document is in a folder of the same name as the document, it will throw an + // error about not being able to use an output filename of '...' as it's a folder. + // This is probably a bug as the --no-codegen flag is set, there is no output. + // + // Error: can't use `...` as output filename because it's a directory + // + const output = process.platform === "win32" ? "nul" : "/dev/null" + + const cmd = `${shellEscape(compiler)} build ${shellEscape(main)} --no-debug --no-color --no-codegen --error-trace -f json -o ${output} ${config.get("flags")}` + + crystalOutputChannel.appendLine(`[Problems] (${getWorkspaceFolder(document.uri).name}) $ ` + cmd) + await execAsync(cmd, folder) + .then((response) => { + diagnosticCollection.clear() + crystalOutputChannel.appendLine("[Problems] No problems found.") + }).catch((err) => { + findProblems(err.stderr, document.uri) + try { + const parsed = JSON.parse(err.stderr) + crystalOutputChannel.appendLine(`[Problems] Error: ${err.stderr}`) + } catch { + crystalOutputChannel.appendLine(`[Problems] Error: ${JSON.stringify(err)}`) + } + }); +} diff --git a/src/spec.ts b/src/spec.ts new file mode 100644 index 0000000..efb462d --- /dev/null +++ b/src/spec.ts @@ -0,0 +1,431 @@ +import { tests, TestItem, Range, Position, Uri, WorkspaceFolder, workspace, TestRunProfileKind, TestMessage, TestRun, TestRunRequest } from "vscode"; +import * as junit2json from 'junit2json'; +import * as path from 'path'; +import { setStatusBar, crystalOutputChannel, getWorkspaceFolder, execAsync, findProblems, getCompilerPath, shellEscape, getCrystalVersion } from "./tools"; +import { existsSync, readFile } from "fs"; +import { Mutex } from "async-mutex"; +import temp = require("temp"); + +const spec_runner_mutex = new Mutex(); + +enum ItemType { + File, + TestCase +} + +export class CrystalTestingProvider { + private workspaceFolders: WorkspaceFolder[] + private controller = tests.createTestController( + 'crystalSpecs', + 'Crystal Specs' + ) + + constructor() { + this.refreshSpecWorkspaceFolders() + this.refreshTestCases() + + + workspace.onDidSaveTextDocument(e => { + if (e.uri.scheme === "file" && this.isSpecFile(e.uri.fsPath)) { + this.deleteTestItem(e.uri.fsPath); + this.getTestCases(getWorkspaceFolder(e.uri), [e.uri.fsPath]) + } + }); + + workspace.onDidChangeWorkspaceFolders((event) => { + this.refreshSpecWorkspaceFolders() + for (var i = 0; i < event.added.length; i += 1) { + crystalOutputChannel.appendLine("Adding folder to workspace: " + event.added[i].uri.fsPath) + this.getTestCases(event.added[i]) + } + event.removed.forEach((folder) => { + crystalOutputChannel.appendLine("Removing folder from workspace: " + folder.uri.fsPath) + this.deleteWorkspaceChildren(folder) + }) + }); + + this.controller.refreshHandler = async () => { + this.refreshSpecWorkspaceFolders() + this.controller.items.forEach((item) => { + this.controller.items.delete(item.id) + }) + this.refreshTestCases() + } + } + + isSpecFile(file: string): boolean { + return file.endsWith('_spec.cr') && + this.workspaceFolders.includes(getWorkspaceFolder(Uri.file(file))) + } + + refreshSpecWorkspaceFolders(): void { + let folders = [] + workspace.workspaceFolders.forEach((folder) => { + if (existsSync(`${folder.uri.fsPath}${path.sep}shard.yml`) && + existsSync(`${folder.uri.fsPath}${path.sep}spec`)) { + folders.push(folder) + } + }) + this.workspaceFolders = folders + } + + async refreshTestCases(): Promise { + return new Promise(async () => { + for (var i = 0; i < this.workspaceFolders.length; i++) { + await this.getTestCases(this.workspaceFolders[i]) + } + }) + } + + async getTestCases(workspace: WorkspaceFolder, paths?: string[]): Promise { + if (spec_runner_mutex.isLocked()) { + return; + } + + const release = await spec_runner_mutex.acquire(); + const dispose = setStatusBar('searching for specs...'); + + await spawnSpecTool(workspace, true, paths) + .then((junit) => { + if (junit) this.convertJunitTestcases(junit) + }).finally(() => { + release() + dispose() + }) + } + + async execTestCases(workspace: WorkspaceFolder, paths?: string[]): Promise { + return spawnSpecTool(workspace, false, paths) + } + + private deleteTestItem(id: string) { + this.controller.items.forEach((child) => { + var item = this.getChild(id, child); + if (item !== undefined) { + item.children.forEach((c) => { + item.children.delete(c.id); + }); + } else if (child.id === id) { + this.controller.items.delete(id) + } + }); + } + + runProfile = this.controller.createRunProfile('Run', TestRunProfileKind.Run, + async (request, token) => { + if (spec_runner_mutex.isLocked()) { + return; + } + + const release = await spec_runner_mutex.acquire(); + + const run = this.controller.createTestRun(request); + const start = Date.now(); + + try { + let runnerArgs = [] + this.controller.items.forEach((item) => { + let generated = this.generateRunnerArgs(item, request.include, request.exclude) + if (generated.length > 0 && !(runnerArgs.includes(generated))) { + runnerArgs = runnerArgs.concat(generated) + } + }) + + let workspaces: WorkspaceFolder[] = [] + runnerArgs.forEach((arg) => { + const uri = Uri.file(arg) + const space = getWorkspaceFolder(uri) + if (space !== undefined && !workspaces.includes(space)) { + workspaces.push(space) + } + }) + + if (token.isCancellationRequested) { + return; + } + + for (var i = 0; i < workspaces.length; i++) { + let args = [] + runnerArgs.forEach((arg) => { + if (workspaces[i] == getWorkspaceFolder(Uri.file(arg)) && !(args.includes(arg))) { + args.push(arg) + } + }) + + await this.execTestCases(workspaces[i], args) + .then(result => { + if (result) this.parseTestCaseResults(result, request, run); + }).catch(err => { + crystalOutputChannel.appendLine("[Spec] Error: " + err.message) + run.end() + }); + + if (token.isCancellationRequested) { + return; + } + } + } finally { + release(); + crystalOutputChannel.appendLine(`Finished execution in ${Date.now() - start}ms`) + run.end(); + } + + } + ); + + private parseTestCaseResults(result: TestSuite, request: TestRunRequest, run: TestRun) { + result.testcase.forEach((testcase: TestCase) => { + let exists: TestItem = undefined; + this.controller.items.forEach((child: TestItem) => { + if (exists === undefined) { + exists = this.getChild(testcase.file + " " + testcase.name, child); + } + }); + + if (exists) { + if (!(request.include && request.include.includes(exists)) || !(request.exclude?.includes(exists))) { + if (testcase.error) { + run.failed(exists, + new TestMessage( + testcase.error.map((v) => { + return this.formatErrorMessage(v) + }).join("\n\n\n") + ), + testcase.time * 1000); + } else if (testcase.failure) { + run.failed(exists, + new TestMessage( + testcase.failure.map((v) => { + return this.formatErrorMessage(v) + }).join("\n\n\n") + ), + testcase.time * 1000); + } else { + run.passed(exists, testcase.time * 1000); + } + } + } + }); + } + + private formatErrorMessage(v: junit2json.Details): string { + return `\n ${v.message.replace("\n", "\n ")}\n\n${v.inner}` + } + + generateRunnerArgs(item: TestItem, includes: readonly TestItem[], excludes: readonly TestItem[]): string[] { + if (includes) { + if (includes.includes(item)) { + return [item.uri.fsPath] + } else { + let foundChildren = [] + item.children.forEach((child) => { + foundChildren = foundChildren.concat(this.generateRunnerArgs(child, includes, excludes)) + }) + return foundChildren + } + } else if (excludes.length > 0) { + if (excludes.includes(item)) { + return [] + } else { + let foundChildren = [] + item.children.forEach((child) => { + foundChildren = foundChildren.concat(this.generateRunnerArgs(child, includes, excludes)) + }) + return foundChildren + } + } else { + return [item.uri.fsPath] + } + } + + private deleteWorkspaceChildren(workspace: WorkspaceFolder) { + this.controller.items.forEach((child) => { + if (child.uri.fsPath.startsWith(workspace.uri.fsPath)) { + this.deleteTestItem(child.uri.fsPath) + } + }) + } + + getChild(id: string, parent: TestItem): TestItem | undefined { + let foundChild = parent.children.get(id) + if (foundChild) { + return foundChild + } + parent.children.forEach((child) => { + if (foundChild === undefined) { + foundChild = this.getChild(id, child) + } + }) + return foundChild + } + + convertJunitTestcases(testsuite: TestSuite): Promise { + return new Promise((resolve, reject) => { + try { + if (testsuite.tests === 0) { + crystalOutputChannel.appendLine(`[Spec] Error: No testcases in testsuite ${JSON.stringify(testsuite)}`) + return + } + + testsuite.testcase.forEach((testcase: TestCase) => { + const item = this.controller.createTestItem( + testcase.file + " " + testcase.name, + testcase.name, + Uri.file(testcase.file) + ) + + if (testcase.hasOwnProperty('line')) { + item.range = new Range( + new Position(testcase.line - 1, 0), + new Position(testcase.line - 1, 0) + ); + } + + let fullPath = getWorkspaceFolder(Uri.file(testcase.file)).uri.fsPath + + path.sep + 'spec'; + let parent: TestItem | null = null + + // split the testcase.file and iterate over every folder in workspace + testcase.file.replace(fullPath, "").split(path.sep).filter((folder => folder !== "")).forEach((node: string) => { + // build full path of folder + fullPath += path.sep + node + + // check if folder exists in test controller + const exists = this.controller.items.get(fullPath) + if (exists) { + // if it does, get it + parent = exists + } else if (parent) { + let childMatch = null + parent.children.forEach((child) => { + if (childMatch === null && child.id === fullPath) { + childMatch = child + } + }) + + if (childMatch !== null) { + parent = childMatch + } else { + // if it doesn't and has a parent, create an item and make it a child of the parent + let child = this.controller.createTestItem(fullPath, node, Uri.file(fullPath)) + parent.children.add(child) + parent = child + } + } else { + // if don't already have a parent, use controller.items + let child = this.controller.createTestItem(fullPath, node, Uri.file(fullPath)) + this.controller.items.add(child) + parent = child + } + }) + + // add testcases to last parent + parent.children.add(item) + }) + resolve() + + } catch (err) { + crystalOutputChannel.appendLine(`[Spec] Error: ${err.message}`) + reject(err); + } + }) + } +} + + +export type TestSuite = junit2json.TestSuite & { + tests?: number; + skipped?: number; + errors?: number; + failures?: number; + time?: number; + timestamp?: string; + hostname?: string; + testcase?: TestCase[]; +} + +export type TestCase = junit2json.TestCase & { + file?: string; + classname?: string; + name?: string; + line?: number; + time?: number; +} + +// Runs `crystal spec --junit temp_file` +export async function spawnSpecTool( + space: WorkspaceFolder, + dry_run: boolean = false, + paths?: string[] +): Promise { + // Get compiler stuff + const compiler = await getCompilerPath(); + const compiler_version = await getCrystalVersion(); + const config = workspace.getConfiguration('crystal-lang'); + + // create a tempfile + const tempFile = temp.path({ suffix: ".xml" }) + + // execute crystal spec + var cmd = `${shellEscape(compiler)} spec --junit_output ${shellEscape(tempFile)} --no-color ${config.get("flags")} ${config.get("spec-tags")}`; + // Only valid for Crystal >= 1.11 + if (dry_run && compiler_version.minor > 10) { + cmd += ` --dry-run` + } + if (paths) { + cmd += ` ${paths.map((i) => shellEscape(i)).join(" ")}` + } + crystalOutputChannel.appendLine(`[Spec] (${space.name}) $ ` + cmd); + + await execAsync(cmd, space.uri.fsPath).catch((err) => { + if (err.stderr) { + findProblems(err.stderr, undefined) + } else if (err.message) { + crystalOutputChannel.appendLine(`[Spec] Error: ${err.message}`) + } else { + crystalOutputChannel.appendLine(`[Spec] Error: ${err.stdout}`) + } + }); + + return readSpecResults(tempFile).then(async (results) => { + return parseJunit(results); + }).catch((err) => { + if (err.message) { + crystalOutputChannel.appendLine(`[Spec] Error: ${err.message}`) + } else { + crystalOutputChannel.appendLine(`[Spec] Error: ${JSON.stringify(err)}`) + } + }); +} + +function readSpecResults(file: string): Promise { + return new Promise((resolve, reject) => { + try { + if (!existsSync(file)) { + reject(new Error("Test results file doesn't exist")); + return; + } + + readFile(file, (error, data) => { + if (error) { + reject(new Error("Error reading test results file: " + error.message)); + } else { + resolve(data); + } + }) + } catch (err) { + reject(err); + } + }) +} + +function parseJunit(rawXml: Buffer): Promise { + return new Promise(async (resolve, reject) => { + try { + const output = await junit2json.parse(rawXml); + resolve(output as TestSuite); + } catch (err) { + reject(err) + } + }) +} diff --git a/src/symbols.ts b/src/symbols.ts new file mode 100644 index 0000000..7074110 --- /dev/null +++ b/src/symbols.ts @@ -0,0 +1,165 @@ +import { + CancellationToken, + DocumentSelector, + DocumentSymbol, + DocumentSymbolProvider, + ExtensionContext, + languages, + Location, + Position, + ProviderResult, + SymbolKind, + SymbolInformation, + TextDocument, +} from 'vscode'; + +const MODULE_OR_LIB_PATTERN = + /^\s*(?:private\s+)?(?:module|lib)\s+(\w+)[\r\n;]?$/; +const MACRO_PATTERN = + /^\s*(?:private\s+)?macro\s+(\w+)(?:[\w\(\)\*,_]+)?[\r\n;]?$/; +const CLASS_PATTERN = + /^\s*(?:abstract\s+)?(?:private\s+)?class\s+(\w+)(?:\s+<\s+\w+)?[\r\n;]?$/; +const STRUCT_PATTERN = + /^\s*(?:abstract\s+)?(?:private\s+)?(?:struct|record)\s+(\w+)(?:\s+<\s+\w+)?(?:.+do(?:\s+\|.+\|)?)?[\r\n;]?$/; +const CONSTANT_PATTERN = + /^\s*(?:([A-Z0-9_]+)\s+=.+|(?:private\s+)?(?:alias|type)\s+(\w+))[\r\n;]?$/; +const ENUM_OR_UNION_PATTERN = + /^\s*(?:private\s+)?(?:enum|union)\s+(\w+)[\r\n;]?$/; +const DEF_PATTERN = + /^\s*(?:abstract\s+)?(?:(?:private|protected)\s+)?(?:def|fun)\s+(\w+)(?:[\(\)\*:,]+)?.*$/; +const PROPERTY_PATTERN = + /^\s*(?:(?:private|protected)\s+)?(?:class_)?(?:property|getter|setter)(?:!|\?)?\s+(\w+)(?:(?:\s+:\s+\w+)?(?:\s*=.+)?)?(?:,\s*)?[\r\n;]?/; +const IVAR_PATTERN = /^\s*@(\w+)\s+[:=].+[\r\n;]?$/; +const CLASS_IVAR_PATTERN = /^\s*@@(\w+)\s+[:=].+[\r\n;]?$/; +const VARIABLE_PATTERN = /^\s*(\w+)\s+[:=].+[\r\n;]?$/; + +class CrystalDocumentSymbolProvider implements DocumentSymbolProvider { + private symbols: SymbolInformation[]; + private container: string[]; + + provideDocumentSymbols( + document: TextDocument, + token: CancellationToken + ): ProviderResult { + this.symbols = []; + this.container = []; + + if (document.fileName.endsWith(".ecr")) return; + + const lines = document + .getText() + .split(/\r?\n/); + let matches: RegExpExecArray; + + for (let [index, line] of lines.entries()) { + if (/^\s*#.*/.test(line)) continue; + + matches = DEF_PATTERN.exec(line); + if (matches && matches.length) { + this.create( + matches[1], + this.container.length ? SymbolKind.Method : SymbolKind.Function, + document, + index + ); + continue; + } + + matches = MACRO_PATTERN.exec(line); + if (matches && matches.length) { + this.create(matches[1], SymbolKind.Function, document, index); + continue; + } + + matches = CLASS_PATTERN.exec(line); + if (matches && matches.length) { + this.create(matches[1], SymbolKind.Class, document, index); + this.container.push(matches[1]); + continue; + } + + matches = PROPERTY_PATTERN.exec(line); + if (matches && matches.length) { + this.create(matches[1], SymbolKind.Method, document, index); + continue; + } + + matches = STRUCT_PATTERN.exec(line); + if (matches && matches.length) { + this.create(matches[1], SymbolKind.Struct, document, index); + this.container.push(matches[1]); + continue; + } + + matches = MODULE_OR_LIB_PATTERN.exec(line); + if (matches && matches.length) { + this.create(matches[1], SymbolKind.Module, document, index); + this.container.push(matches[1]); + continue; + } + + matches = ENUM_OR_UNION_PATTERN.exec(line); + if (matches && matches.length) { + this.create(matches[1], SymbolKind.Enum, document, index); + this.container.push(matches[1]); + continue; + } + + matches = CONSTANT_PATTERN.exec(line); + if (matches && matches.length) { + this.create(matches[1], SymbolKind.Constant, document, index); + continue; + } + + matches = IVAR_PATTERN.exec(line); + if (matches && matches.length) { + this.create(matches[1], SymbolKind.Property, document, index); + continue; + } + + matches = CLASS_IVAR_PATTERN.exec(line); + if (matches && matches.length) { + this.create(matches[1], SymbolKind.Property, document, index); + continue; + } + + matches = VARIABLE_PATTERN.exec(line); + if (matches && matches.length) { + this.create(matches[1], SymbolKind.Variable, document, index); + continue; + } + + if (/\s*end(?:\..*)?$/.test(line)) { + this.container.pop(); + continue; + } + } + + // crystalOutputChannel.appendLine(this.symbols); + return this.symbols; + } + + private create( + name: string, + kind: SymbolKind, + { uri }: TextDocument, + index: number + ): void { + const loc = new Location(uri, new Position(index, 0)); + this.symbols.push( + new SymbolInformation(name, kind, this.container[index - 1], loc) + ); + } +} + +export function registerSymbols( + selector: DocumentSelector, + context: ExtensionContext +): void { + context.subscriptions.push( + languages.registerDocumentSymbolProvider( + selector, + new CrystalDocumentSymbolProvider() + ) + ); +} diff --git a/src/tasks.ts b/src/tasks.ts new file mode 100644 index 0000000..ed7ee65 --- /dev/null +++ b/src/tasks.ts @@ -0,0 +1,280 @@ +import { ExtensionContext, Task, TaskDefinition, ShellExecution, ShellExecutionOptions, TextDocument, WorkspaceFolder, WorkspaceFoldersChangeEvent, TaskGroup, TaskPresentationOptions, TaskRevealKind, Disposable, Uri, workspace, TaskProvider, tasks, TaskPanelKind } from "vscode" +import { getCompilerPath, getMainFile, getShardsPath, getWorkspaceFolder } from "./tools" +import * as path from "path" + +// Copy from https://github.com/rust-lang/rls-vscode/blob/master/src/tasks.ts +export function registerTasks(context: ExtensionContext): void { + workspace.onDidOpenTextDocument((doc) => DidOpenTextDocument(doc, context)) + workspace.textDocuments.forEach((doc) => DidOpenTextDocument(doc, context)) + workspace.onDidChangeWorkspaceFolders((e) => didChangeWorkspaceFolders(e, context)); +} + +export function DidOpenTextDocument(document: TextDocument, context: ExtensionContext): void { + if (document.languageId !== 'crystal') { + return + } + + const uri = document.uri + let folder = getWorkspaceFolder(uri) + if (!folder) { + return + } + + folder = getOuterMostWorkspaceFolder(folder); + if (!workspaces.has(folder.uri.toString())) { + const client = new CrystalTaskClient(folder) + workspaces.set(folder.uri.toString(), client) + client.start(context) + } +} + +export function getOuterMostWorkspaceFolder(folder: WorkspaceFolder): WorkspaceFolder { + const sorted = sortedWorkspaceFolders(); + for (const element of sorted) { + let uri = folder.uri.toString(); + if (uri.charAt(uri.length - 1) !== path.sep) { + uri = uri + path.sep; + } + if (uri.startsWith(element)) { + return getWorkspaceFolder(Uri.parse(element)) || folder; + } + } + return folder; +} + +// This is an intermediate, lazy cache used by `getOuterMostWorkspaceFolder` +// and cleared when VSCode workspaces change. +let _sortedWorkspaceFolders: string[] | undefined; + +export function sortedWorkspaceFolders(): string[] { + if (!_sortedWorkspaceFolders && workspace.workspaceFolders) { + _sortedWorkspaceFolders = workspace.workspaceFolders.map(folder => { + let result = folder.uri.toString(); + if (result.charAt(result.length - 1) !== path.sep) { + result = result + path.sep; + } + return result; + }).sort( + (a, b) => { + return a.length - b.length; + } + ); + } + return _sortedWorkspaceFolders || []; +} + +export function didChangeWorkspaceFolders(e: WorkspaceFoldersChangeEvent, context: ExtensionContext): void { + _sortedWorkspaceFolders = undefined; + + // If a VSCode workspace has been added, check to see if it is part of an existing one, and + // if not, and it is a Rust project (i.e., has a Cargo.toml), then create a new client. + for (let folder of e.added) { + folder = getOuterMostWorkspaceFolder(folder); + if (workspaces.has(folder.uri.toString())) { + continue; + } + + const client = new CrystalTaskClient(folder) + workspaces.set(folder.uri.toString(), client) + client.start(context) + } + + // If a workspace is removed which is a Crystal workspace, kill the client. + for (const folder of e.removed) { + const client = workspaces.get(folder.uri.toString()); + if (client) { + workspaces.delete(folder.uri.toString()); + client.stop() + } + } +} + +const workspaces: Map = new Map(); + +class CrystalTaskClient { + readonly folder: WorkspaceFolder; + disposables: Disposable[]; + + constructor(folder: WorkspaceFolder) { + this.folder = folder + this.disposables = [] + } + + async start(context: ExtensionContext) { + this.disposables.push(this.registerTaskProvider(TaskType.Crystal)) + this.disposables.push(this.registerTaskProvider(TaskType.Shards)) + } + + async stop() { + let promise: Thenable = Promise.resolve(void 0) + return promise.then(() => { + this.disposables.forEach(d => d.dispose()) + }) + } + + registerTaskProvider(taskType: TaskType): Disposable { + let provider: TaskProvider = new CrystalTaskProvider(taskType, this.folder); + const disposable = tasks.registerTaskProvider(taskType, provider); + return disposable + } +} + +enum TaskType { + Crystal = 'crystal', + Shards = 'shards', +} + +class CrystalTaskProvider implements TaskProvider { + constructor(private _taskType: TaskType, private _workspaceFolder: WorkspaceFolder) { + } + + public provideTasks() { + return getCrystalTasks(this._taskType, this._workspaceFolder) + } + + public resolveTask(_task: Task): Task | undefined { + return undefined + } +} + +interface CrystalTaskDefinition extends TaskDefinition { + label: string + command: string + args?: Array + file?: string +} + +interface TaskConfigItem { + definition: CrystalTaskDefinition + problemMatcher: Array + group?: TaskGroup + presentationOptions?: TaskPresentationOptions +} + +async function getCrystalTasks(taskType: TaskType, target: WorkspaceFolder): Promise { + const taskList = createCrystalTaskConfigItem(taskType, target) + const list = await Promise.all(taskList.map(async (def) => { + const task: Task = await createCrystalTask(def, target) + return task + })) + + return list +} + +async function createCrystalTask({ definition, group, presentationOptions, problemMatcher }: TaskConfigItem, target: WorkspaceFolder): Promise { + let taskBin = await getCompilerPath() + let taskArgs: Array = (definition.args !== undefined) ? definition.args : [] + if (definition.file !== undefined) { + taskArgs.push(definition.file) + } + + let source = 'Crystal' + if (definition.type !== 'crystal') { + taskBin = await getShardsPath() + source = 'Shards' + } + + const execCmd = `${taskBin} ${definition.command} ${taskArgs}` + const execOption: ShellExecutionOptions = { + cwd: target.uri.fsPath + } + const exec = new ShellExecution(execCmd, execOption) + let label = definition.label + if (definition.type == 'crystal' && definition.file !== undefined) { + label = `${label} - ${definition.file}` + } + + const task = new Task(definition, target, label, source, exec, problemMatcher) + if (group !== undefined) { + task.group = group + } + + if (presentationOptions !== undefined) { + task.presentationOptions = presentationOptions + } + + return task +} + +const CRYSTAL_TASKS: Array<{ type: string, command: string, args?: Array, group: TaskGroup }> = [ + { + type: 'crystal', + command: 'run', + group: TaskGroup.Build + }, + { + type: 'crystal', + command: 'docs', + group: TaskGroup.Clean + }, + { + type: 'crystal', + command: 'tool format', + group: TaskGroup.Build + }, + { + type: 'crystal', + command: 'tool unreachable', + group: TaskGroup.Build + }, + { + type: 'crystal', + command: 'spec', + group: TaskGroup.Test + }, + { + type: 'shards', + command: 'install', + group: TaskGroup.Build + }, + { + type: 'shards', + command: 'update', + group: TaskGroup.Build + }, + { + type: 'shards', + command: 'build', + args: [ + '--release' + ], + group: TaskGroup.Build + }, + { + type: 'shards', + command: 'prune', + group: TaskGroup.Clean + } +] + +function createCrystalTaskConfigItem(taskType: TaskType, target: WorkspaceFolder): Array { + const problemMatcher = [] + const presentationOptions: TaskPresentationOptions = { + reveal: TaskRevealKind.Always, + panel: TaskPanelKind.Dedicated, + } + const mainFile = getMainFile(target) + const tasks = CRYSTAL_TASKS.filter((task) => task.type === taskType).map((opt) => { + const def: CrystalTaskDefinition = { + label: opt.command, + type: opt.type, + command: opt.command, + args: opt.args, + } + + if (opt.type == 'crystal' && opt.group == TaskGroup.Build) { + def.file = mainFile + } + + const task = { + definition: def, + problemMatcher, + group: def.group, + presentationOptions + } + + return task + }) + + return tasks +} diff --git a/src/tools.ts b/src/tools.ts new file mode 100644 index 0000000..fa8a398 --- /dev/null +++ b/src/tools.ts @@ -0,0 +1,534 @@ +import { ChildProcess, ExecException, exec } from 'child_process'; +import { existsSync, readFileSync } from 'fs'; +import * as path from 'path'; +import { promisify } from 'util'; +import { Position, TextDocument, WorkspaceFolder, window, workspace, Uri, languages, Range, Diagnostic, DiagnosticSeverity } from 'vscode'; +import * as yaml from 'yaml'; +import { cwd } from 'process'; +import { Mutex } from 'async-mutex'; + +export const crystalOutputChannel = window.createOutputChannel("Crystal", "log") + +export const compiler_mutex: Mutex = new Mutex(); + +/** + * Wrapper for `exec`. + * + * @param {string} command full command to execute + * @param {string} cwd directory to execute this in + * @param {(( + * error: (ExecException & { stdout: string; stderr: string }) | {}, + * stdout: string, + * stderr: string + * ) => void)} [callback] + * @return {*} {ChildProcess} + */ +function execWrapper( + command: string, + cwd: string, + callback?: ( + error: (ExecException & { stdout: string; stderr: string }) | {}, + stdout: string, + stderr: string + ) => void +): ChildProcess { + const response = exec(command, { 'cwd': cwd }, (err, stdout, stderr) => { + if (err) { + callback({ ...err, stderr, stdout }, stdout, stderr); + return; + } + + callback(err, stdout, stderr); + }); + + return response; +} + +export const execAsync = promisify(execWrapper); + +/** + * Promisify of `execWrapper`. + * + * @export + * @param {string} message + * @return {*} {() => void} + */ +export function setStatusBar(message: string): () => void { + const bar = window.setStatusBarMessage(`Crystal: ${message} $(loading~spin)`); + return () => bar.dispose(); +} + +/** + * Gets the path of the Crystal compiler on the system. + * + * @export + * @return {*} {Promise} + */ +export async function getCompilerPath(): Promise { + const config = workspace.getConfiguration('crystal-lang'); + + if (config.has('compiler')) { + const exe = config.get('compiler'); + if (path.isAbsolute(exe) && existsSync(exe)) return Promise.resolve(exe); + } + + const command = + (process.platform === 'win32' ? 'where' : 'which') + ' crystal'; + + return (await execAsync(command, process.cwd())).trim(); +} + +/** + * Gets the path of the shards executable on the system. + * + * @export + * @return {*} {Promise} + */ +export async function getShardsPath(): Promise { + const config = workspace.getConfiguration('crystal-lang'); + + if (config.has('shards')) { + const exe = config.get('shards'); + if (path.isAbsolute(exe) && existsSync(exe)) return Promise.resolve(exe); + } + + const command = + (process.platform === 'win32' ? 'where' : 'which') + ' shards'; + + return (await execAsync(command, process.cwd())).trim(); +} + +/** + * Gets the first target from a workspace folders `shard.yml` if it exists, + * otherwise returns a glob of all files in `src/*`. + * + * @export + * @param {WorkspaceFolder} folder + * @return {*} {string} + */ +export function getMainFile(folder: WorkspaceFolder): string { + const shardFile = getShardFile(folder) + if (existsSync(shardFile)) { + const io = readFileSync(shardFile, 'utf8') + const data = yaml.parse(io) + + if (data.targets !== undefined) { + const values = Object.keys(data.targets).map(key => data.targets[key]) + // NOTE: match first targets + if (values.length > 0) { + return values[0].main + } + } + } + + const defaultMainFile = workspace.getConfiguration('crystal-lang', folder.uri).get('main', 'src/*.cr') + return defaultMainFile +} + +/** + * Gets the `shard.yml` (if it exists) for a given workspace folder. + * + * @param {WorkspaceFolder} workspace + * @return {*} {string} + */ +function getShardFile(workspace: WorkspaceFolder): string { + return workspace.uri.fsPath + path.sep + 'shard.yml' +} + +/** + * Returns the position of a cursor in a file in the form `/path/to/file:line:col` + * for use by Crystal compiler tools. + * + * @export + * @param {TextDocument} document + * @param {Position} position + * @return {*} {string} + */ +export function getCursorPath(document: TextDocument, position: Position): string { + // https://github.com/crystal-lang/crystal/issues/13086 + // return `${document.fileName}:${position.line + 1}:${position.character + 1}`; + const path = `${document.fileName}:${position.line + 1}:${position.character + 1 + }`; + if (/^\w:\\/.test(path)) return path.slice(2); + return path; +} + +/** + * Format for an individual dependency in a `shard.yml`. + * + * @interface Dependency + */ +interface Dependency { + git?: string; + github?: string; + gitlab: string; + branch?: string; + version?: string; +} + +/** + * Format of a `shard.yml` file. + * + * @interface Shard + */ +interface Shard { + name: string; + description?: string; + version: string; + crystal?: string; + repository?: string; + authors?: string[]; + dependencies?: Record; + targets?: Record>; + license?: string; +} + +// Takes in a vscode TextDocument and returns the relevant +// entrypoint for it in its workspace folder + +/** + * Determines the relevant entrypoint for a Crystal file. + * If it is a spec, the file itself is returned. If a main file is set + * in the workspace config, that is returned. If there's a `shard.yml` + * at the root of the workspace, it then checks targrets using the depedencies tool, + * otherwise falls back to the first target in the `shard.yml`. If there are no targets, + * a glob of all the files in `src/*` is returned. + * Otherwise if there's no `shard.yml`, the file itself is returned. + * + * @export + * @param {TextDocument} document + * @return {*} {Promise} + */ +export async function getShardMainPath(document: TextDocument): Promise { + const config = workspace.getConfiguration('crystal-lang'); + const space = getWorkspaceFolder(document.uri); + const dir = space.uri.fsPath; + const fp = path.join(dir, 'shard.yml'); + + // Specs are their own main files + if (document.fileName.endsWith('_spec.cr')) { + return document.fileName; + } + + // Use main if provided and it exists + if (config.get("main")) { + const main = config.get("main").replace("${workspaceRoot}", dir) + if (main.includes('*') || existsSync(main)) return main; + } + + if (config.get("dependencies")) { + const shardTarget = await getShardTargetForFile(document) + if (shardTarget) return shardTarget; + } + + // If this is a crystal project + if (existsSync(fp)) { + const shard_yml = readFileSync(fp, 'utf-8') + const shard = yaml.parse(shard_yml) as Shard; + + // Use a target with the shard name + var main = shard.targets?.[shard.name]?.main; + if (main && existsSync(path.resolve(dir, main))) return path.resolve(dir, main); + + if (shard.targets) { + // Use the first target if it exists + main = Object.values(shard.targets)[0]?.main; + if (main && existsSync(path.resolve(dir, main))) return path.resolve(dir, main); + } + + // Splat all top-level files in source folder, + // only if the file is in the /src directory + const document_path = document.uri.fsPath; + if (document_path.includes(path.join(dir, 'src')) || document_path.includes(path.join(dir, 'lib'))) { + return path.join(space.uri.fsPath, "src", "*.cr"); + } + } + + // https://github.com/crystal-lang/crystal/issues/13086 + // return document.fileName; + if (/^\w:\\/.test(document.fileName)) return document.fileName.slice(2); + + // single independent file (like a script) + return document.fileName; +} + +/** + * Gets the Crystal source library path, i.e. `/usr/bin/../share/crystal/src`. + * + * @export + * @return {*} {Promise} + */ +export async function getCrystalLibPath(): Promise { + const compiler = await getCompilerPath(); + const libpath = await execAsync(`${compiler} env CRYSTAL_PATH`, process.cwd()); + + return libpath.replace(/^lib[:;]|(?:\r)?\n/g, ''); +} + +/** + * Gets the main entrypoint for a given shard in the `lib/` folder of a workspace. + * + * @export + * @param {TextDocument} document + * @param {string} name + * @return {*} {(string | undefined)} + */ +export function getMainForShard( + document: TextDocument, + name: string +): string | undefined { + const dir = getWorkspaceFolder(document.uri).uri.fsPath; + const fp = path.join(dir, 'lib', name, 'shard.yml'); + crystalOutputChannel.appendLine(fp); + if (!existsSync(fp)) return; + + const shard = yaml.parse(fp) as Shard; + const main = shard.targets?.[shard.name]?.main; + if (main) return path.resolve(dir, main); + + const mp = path.join(dir, 'lib', name, 'src', name + '.cr'); + if (existsSync(mp)) return mp; +} + +/** + * Semantic version of Crystal + * + * @export + * @interface SemVer + */ +export interface SemVer { + major: number, + minor: number, + patch: number +} + +/** + * Gets the version of the Crystal compiler. + * + * @export + * @return {*} {Promise} + */ +export async function getCrystalVersion(): Promise { + const compiler = await getCompilerPath(); + const cmd = `${shellEscape(compiler)} --version` + const response = await execAsync(cmd, cwd()) + + const match = response.match(/Crystal (\d+)\.(\d+)\.(\d+)/) + + return { + major: Number(match[1]), + minor: Number(match[2]), + patch: Number(match[3]) + } +} + +export const diagnosticCollection = languages.createDiagnosticCollection("crystal") + +/** + * Interface for how errors are returned by the Crystal compiler. + * + * @interface ErrorResponse + */ +interface ErrorResponse { + file: string + line: number | null + column: number | null + size: number | null + message: string +} + +/** + * Searches the response for JSON formatted errors. If the response is not JSON, calls + * `findProblemsRaw` on the response. + * + * @export + * @param {string} response output of Crystal compiler & tools + * @param {Uri} uri the file these errors correspond to + * @return {*} {Promise} + */ +export async function findProblems(response: string, uri: Uri): Promise { + const space = getWorkspaceFolder(uri); + + let diagnostics = [] + var parsedResponses: ErrorResponse[]; + try { + parsedResponses = JSON.parse(response) + } catch { + return await findProblemsRaw(response, uri) + } + + if (!JSON.parse(response).status) { + for (let resp of parsedResponses) { + if (resp.line == null) + resp.line = 1 + if (resp.column == null) + resp.column = 1 + if (resp.size == null) + resp.size = 0 + const range = new Range(resp.line - 1, resp.column - 1, resp.line - 1, (resp.column + resp.size) - 1) + const diagnostic = new Diagnostic(range, resp.message, DiagnosticSeverity.Error) + var diag_uri = Uri.file(resp.file) + if (!path.isAbsolute(resp.file)) { + diag_uri = Uri.file(path.resolve(space.uri.fsPath, resp.file)) + } + + diagnostics.push([diag_uri, [diagnostic]]) + } + } + + + if (diagnostics.length == 0) { + diagnosticCollection.clear() + } else { + diagnosticCollection.set(diagnostics) + } +} + +/** + * Searches the rersponse for raw syntax errors. + * + * @export + * @param {string} response output of the Crystal compiler * tools + * @param {Uri} uri the file these errors correspond to + * @return {*} {Promise} + */ +export async function findProblemsRaw(response: string, uri: Uri): Promise { + if (!response) return; + + const space = getWorkspaceFolder(uri); + const responseData = response.match(/(?:.*)in '?(.*):(\d+):(\d+)'?:?([^]*)$/mi) + + let parsedLine = 0 + try { + parsedLine = parseInt(responseData[1]) + } catch { + diagnosticCollection.delete(uri) + return; + } + + let diagnostics = [] + if (parsedLine != 0) { + const resp: ErrorResponse = { + file: (uri && uri.fsPath) || responseData[1], + line: parseInt(responseData[2]), + column: parseInt(responseData[3]), + size: null, + message: responseData[4].trim() + } + + const range = new Range(resp.line - 1, resp.column - 1, resp.line - 1, resp.column - 1) + const diagnostic = new Diagnostic(range, resp.message, DiagnosticSeverity.Error) + var diag_uri = Uri.file(resp.file) + if (!path.isAbsolute(resp.file)) { + diag_uri = Uri.file(path.resolve(space.uri.fsPath, resp.file)) + } + + diagnostics.push([diag_uri, [diagnostic]]) + } + + if (diagnostics.length == 0) { + diagnosticCollection.clear() + } else { + diagnosticCollection.set(diagnostics) + } +} + +// + +/** + * Escape characters for passing to `exec`. Does not escape '*' as it's needed for some shard mainfiles. + * Borrowed from https://taozhi.medium.com/escape-shell-command-in-nodejs-629ded063535. + * + * @export + * @param {string} arg + * @return {*} {string} + */ +export function shellEscape(arg: string): string { + if (/[^A-Za-z0-9_\/:=-]/.test(arg)) return arg.replace(/([$!'"();`?{}[\]<>&%#~@\\ ])/g, '\\$1') + return arg +} + +// Handle if the current file isn't in a workspace + +/** + * Wrapper for `workspace.getWorkspaceFolder` that returns the parent folder of the uri itself + * if the file is not in a workspace folder. + * + * @export + * @param {Uri} uri + * @return {*} {WorkspaceFolder} + */ +export function getWorkspaceFolder(uri: Uri): WorkspaceFolder { + const folder = workspace.getWorkspaceFolder(uri) + if (folder) return folder; + + return { + name: path.dirname(uri.fsPath), + uri: Uri.file(path.dirname(uri.fsPath)), + index: undefined + } +} + +/** + * Uses `crystal tool dependencies` to find which target in the `shard.yml` corresponds to + * the given file (if one exists). + * + * @export + * @param {TextDocument} document + * @return {*} {Promise} + */ +export async function getShardTargetForFile(document: TextDocument): Promise { + const compiler = await getCompilerPath(); + const space = getWorkspaceFolder(document.uri); + const targets = getShardYmlTargets(space); + const config = workspace.getConfiguration('crystal-lang'); + + if (!targets) return; + + for (const target of targets) { + const targetPath = path.resolve(space.uri.fsPath, target) + if (!existsSync(targetPath)) continue; + + const cmd = `${shellEscape(compiler)} tool dependencies ${shellEscape(targetPath)} -f flat --no-color ${config.get("flags")}` + crystalOutputChannel.appendLine(`[Dependencies] ${space.name} $ ${cmd}`) + + const response = await execAsync(cmd, space.uri.fsPath) + .catch((err) => { + findProblems(err.stderr, document.uri); + crystalOutputChannel.appendLine(`[Dependencies] error: ${err.stderr}`); + }) + + if (!response) continue; + const dependencies = response.split(/\r?\n/) + + for (const line of dependencies) { + if (path.resolve(space.uri.fsPath, line) == document.uri.fsPath) { + return path.resolve(space.uri.fsPath, target); + } + } + } + + return +} + +/** + * Gets the paths to each target in the `shard.yml` of a workspace. + * + * @param {WorkspaceFolder} space + * @return {*} {string[]} + */ +function getShardYmlTargets(space: WorkspaceFolder): string[] { + const shardFile = getShardFile(space) + + if (existsSync(shardFile)) { + const io = readFileSync(shardFile, 'utf8') + const data = yaml.parse(io) + + if (data.targets !== undefined) { + const values = Object.keys(data.targets).map(key => data.targets[key]) + return values.map(v => v.main) + } + } + + return [] +} diff --git a/syntaxes/crystal.json b/syntaxes/crystal.json index 53df024..fad4ef9 100644 --- a/syntaxes/crystal.json +++ b/syntaxes/crystal.json @@ -288,7 +288,7 @@ "patterns": [ { "begin": "(?![\\s,)])", - "end": "(?=,|\\)\\s*$)", + "end": "(?=,|\\)\\s*)", "patterns": [ { "captures": { @@ -513,7 +513,7 @@ { "begin": "%x\\[", "beginCaptures": { - "0": { + "0": { "name": "punctuation.definition.string.begin.crystal" } }, @@ -1035,9 +1035,9 @@ "name": "comment.line.number-sign.crystal" }, { - "match": "\\b_([\\w]+[?!]?)", - "name": "comment.unused.crystal" - }, + "match": "\\b_([\\w]+[?!]?)", + "name": "comment.unused.crystal" + }, { "begin": "(?><<-('?)((?:[_\\w]+_|)HTML)\\b\\1)", "beginCaptures": { @@ -1867,4 +1867,4 @@ ] } } -} \ No newline at end of file +} diff --git a/syntaxes/ecr.json b/syntaxes/ecr.json index 4097fb0..af3eb7b 100644 --- a/syntaxes/ecr.json +++ b/syntaxes/ecr.json @@ -30,4 +30,4 @@ "include": "text.html.basic" } ] -} \ No newline at end of file +} diff --git a/syntaxes/slang.json b/syntaxes/slang.json index 77021df..f189c60 100644 --- a/syntaxes/slang.json +++ b/syntaxes/slang.json @@ -535,4 +535,4 @@ }, "scopeName": "text.slang", "uuid": "36302CC1-1E76-4910-B7B6-F1915EBBA0D3" -} \ No newline at end of file +} diff --git a/test/extension.test.ts b/test/extension.test.ts deleted file mode 100644 index 2508f56..0000000 --- a/test/extension.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -// -// Note: This example test is leveraging the Mocha test framework. -// Please refer to their documentation on https://mochajs.org/ for help. -// - -// The module 'assert' provides assertion methods from node -import * as assert from 'assert'; - -// You can import and use all API from the 'vscode' module -// as well as import your extension to test it -import * as vscode from 'vscode'; -import * as utils from '../src/crystalUtils'; -import { isNotKeyword } from '../src/crystalUtils'; - -var config = vscode.workspace.getConfiguration("crystal-lang"); - -// Defines a Mocha test suite to group tests of similar kind together -suite("Common Utils", () => { - - test("Concurrent class", () => { - assert.equal(utils.Concurrent.counter, 0); - utils.Concurrent.counter += 1 - assert.equal(utils.Concurrent.counter, 1); - assert.equal(utils.Concurrent.limit(), 3); - assert.equal(utils.Concurrent.limit() > utils.Concurrent.counter, true); - utils.Concurrent.counter += 2 - assert.equal(utils.Concurrent.counter, 3); - assert.equal(utils.Concurrent.limit() > utils.Concurrent.counter, false); - }) - - test("Verify keywords", () => { - assert.equal(utils.isNotKeyword("def"), false) - assert.equal(utils.isNotKeyword("foo"), true) - }) - - test("Verify libs", () => { - assert.equal(utils.isNotLib("/foo/bar"), true) - assert.equal(utils.isNotLib("lib"), false) - }) - - // test("Search problems on compiler output", () => { - // assert.equal(utils.searchProblems("", new vscode.Uri).length, 0) - // assert.equal(utils.searchProblems(`[{"file":"","line":1,"column":1,"size":null,"message":"for empty hashes use '{} of KeyType => ValueType'"}]`, new vscode.Uri).length, 1) - // }) - - // ----------------------- - // TODO: create more tests - // ----------------------- -})