diff options
Diffstat (limited to 'src/tools/rust-analyzer/editors/code')
42 files changed, 13008 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/editors/code/.eslintignore b/src/tools/rust-analyzer/editors/code/.eslintignore new file mode 100644 index 00000000000..3a1e8e186f5 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/.eslintignore @@ -0,0 +1,2 @@ +node_modules +.eslintrc.js diff --git a/src/tools/rust-analyzer/editors/code/.eslintrc.js b/src/tools/rust-analyzer/editors/code/.eslintrc.js new file mode 100644 index 00000000000..9705c5f5ec6 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/.eslintrc.js @@ -0,0 +1,46 @@ +module.exports = { + env: { + es6: true, + node: true, + }, + extends: ["prettier"], + parser: "@typescript-eslint/parser", + parserOptions: { + project: true, + tsconfigRootDir: __dirname, + sourceType: "module", + }, + plugins: ["@typescript-eslint"], + rules: { + camelcase: ["error"], + eqeqeq: ["error", "always", { null: "ignore" }], + curly: ["error", "multi-line"], + "no-console": ["error", { allow: ["warn", "error"] }], + "prefer-const": "error", + "@typescript-eslint/member-delimiter-style": [ + "error", + { + multiline: { + delimiter: "semi", + requireLast: true, + }, + singleline: { + delimiter: "semi", + requireLast: false, + }, + }, + ], + "@typescript-eslint/semi": ["error", "always"], + "@typescript-eslint/no-unnecessary-type-assertion": "error", + "@typescript-eslint/no-floating-promises": "error", + + "@typescript-eslint/consistent-type-imports": [ + "error", + { + prefer: "type-imports", + fixStyle: "inline-type-imports", + }, + ], + "@typescript-eslint/no-import-type-side-effects": "error", + }, +}; diff --git a/src/tools/rust-analyzer/editors/code/.gitignore b/src/tools/rust-analyzer/editors/code/.gitignore new file mode 100644 index 00000000000..2c975a947eb --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/.gitignore @@ -0,0 +1,7 @@ +out +node_modules +server +.vscode-test/ +*.vsix +bundle +vscode.proposed.d.ts diff --git a/src/tools/rust-analyzer/editors/code/.prettierignore b/src/tools/rust-analyzer/editors/code/.prettierignore new file mode 100644 index 00000000000..13baf68d7cc --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/.prettierignore @@ -0,0 +1,3 @@ +node_modules +.vscode-test +out diff --git a/src/tools/rust-analyzer/editors/code/.prettierrc.js b/src/tools/rust-analyzer/editors/code/.prettierrc.js new file mode 100644 index 00000000000..cafb12f0e6d --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/.prettierrc.js @@ -0,0 +1,5 @@ +module.exports = { + // use 100 because it's Rustfmt's default + // https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#max_width + printWidth: 100, +}; diff --git a/src/tools/rust-analyzer/editors/code/.vscodeignore b/src/tools/rust-analyzer/editors/code/.vscodeignore new file mode 100644 index 00000000000..09dc27056b3 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/.vscodeignore @@ -0,0 +1,14 @@ +** +!icon.png +!language-configuration.json +!LICENSE +!node_modules/@hpcc-js/wasm/dist/graphvizlib.wasm +!node_modules/@hpcc-js/wasm/dist/index.min.js +!node_modules/d3-graphviz/build/d3-graphviz.min.js +!node_modules/d3/dist/d3.min.js +!out/main.js +!package-lock.json +!package.json +!ra_syntax_tree.tmGrammar.json +!server +!README.md diff --git a/src/tools/rust-analyzer/editors/code/LICENSE b/src/tools/rust-analyzer/editors/code/LICENSE new file mode 100644 index 00000000000..d32e7fe8c91 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/LICENSE @@ -0,0 +1,230 @@ +This software is licensed under either of the Apache License, Version +2.0, or the MIT License. See below for their text. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +MIT License + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/tools/rust-analyzer/editors/code/README.md b/src/tools/rust-analyzer/editors/code/README.md new file mode 100644 index 00000000000..36ab9818822 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/README.md @@ -0,0 +1,52 @@ +# rust-analyzer + +This extension provides support for the [Rust programming language](https://www.rust-lang.org/). +It is recommended over and replaces `rust-lang.rust`. + +## Features + +- [code completion] with [imports insertion] +- go to [definition], [implementation], [type definition] +- [find all references], [workspace symbol search], [symbol renaming] +- [types and documentation on hover] +- [inlay hints] for types and parameter names +- [semantic syntax highlighting] +- a lot of [assists (code actions)] +- apply suggestions from errors +- ... and many more, check out the [manual] to see them all + +[code completion]: https://rust-analyzer.github.io/manual.html#magic-completions +[imports insertion]: https://rust-analyzer.github.io/manual.html#completion-with-autoimport +[definition]: https://rust-analyzer.github.io/manual.html#go-to-definition +[implementation]: https://rust-analyzer.github.io/manual.html#go-to-implementation +[type definition]: https://rust-analyzer.github.io/manual.html#go-to-type-definition +[find all references]: https://rust-analyzer.github.io/manual.html#find-all-references +[workspace symbol search]: https://rust-analyzer.github.io/manual.html#workspace-symbol +[symbol renaming]: https://rust-analyzer.github.io/manual.html#rename +[types and documentation on hover]: https://rust-analyzer.github.io/manual.html#hover +[inlay hints]: https://rust-analyzer.github.io/manual.html#inlay-hints +[semantic syntax highlighting]: https://rust-analyzer.github.io/manual.html#semantic-syntax-highlighting +[assists (code actions)]: https://rust-analyzer.github.io/manual.html#assists-code-actions +[manual]: https://rust-analyzer.github.io/manual.html + +## Quick start + +1. Install [rustup]. +2. Install the [rust-analyzer extension]. + +[rustup]: https://rustup.rs +[rust-analyzer extension]: https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer + +## Configuration + +This extension provides configurations through VSCode's configuration settings. All configurations are under `rust-analyzer.*`. + +See [the manual](https://rust-analyzer.github.io/manual.html#vs-code-2) for more information on VSCode specific configurations. + +## Communication + +For usage and troubleshooting requests, please use the ["IDEs and Editors" category of the Rust forum](https://users.rust-lang.org/c/ide/14). + +## Documentation + +See [rust-analyzer.github.io](https://rust-analyzer.github.io/) for more information. diff --git a/src/tools/rust-analyzer/editors/code/icon.png b/src/tools/rust-analyzer/editors/code/icon.png new file mode 100644 index 00000000000..072090c6f4a --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/icon.png Binary files differdiff --git a/src/tools/rust-analyzer/editors/code/language-configuration.json b/src/tools/rust-analyzer/editors/code/language-configuration.json new file mode 100644 index 00000000000..6619d0c85c5 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/language-configuration.json @@ -0,0 +1,46 @@ +{ + "comments": { + "lineComment": "//", + "blockComment": ["/*", "*/"] + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "colorizedBracketPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + { "open": "{", "close": "}" }, + { "open": "[", "close": "]" }, + { "open": "(", "close": ")" }, + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "/*", "close": " */", "notIn": ["string"] }, + { "open": "`", "close": "`", "notIn": ["string"] }, + { "open": "```", "close": "```", "notIn": ["string"] } + ], + "autoCloseBefore": ";:.,=}])> \n\t", + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["<", ">"], + ["\"", "\""], + ["'", "'"], + ["`", "`"], + ["```", "```"] + ], + "indentationRules": { + "increaseIndentPattern": "^.*\\{[^}\"']*$|^.*\\([^\\)\"']*$", + "decreaseIndentPattern": "^\\s*(\\s*\\/[*].*[*]\\/\\s*)*[})]" + }, + "folding": { + "markers": { + "start": "^\\s*// region:\\b", + "end": "^\\s*// endregion\\b" + } + } +} diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json new file mode 100644 index 00000000000..bd8b0e9c4e0 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -0,0 +1,4001 @@ +{ + "name": "rust-analyzer", + "version": "0.5.0-dev", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "rust-analyzer", + "version": "0.5.0-dev", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@hpcc-js/wasm": "^2.13.0", + "anser": "^2.1.1", + "d3": "^7.8.5", + "d3-graphviz": "^5.0.2", + "vscode-languageclient": "^8.1.0" + }, + "devDependencies": { + "@tsconfig/strictest": "^2.0.1", + "@types/node": "~16.11.7", + "@types/vscode": "~1.78.1", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vscode/test-electron": "^2.3.8", + "@vscode/vsce": "^2.19.0", + "esbuild": "^0.18.12", + "eslint": "^8.44.0", + "eslint-config-prettier": "^8.8.0", + "ovsx": "^0.8.2", + "prettier": "^3.0.0", + "tslib": "^2.6.0", + "typescript": "^5.1.6" + }, + "engines": { + "vscode": "^1.78.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.12.tgz", + "integrity": "sha512-LIxaNIQfkFZbTLb4+cX7dozHlAbAshhFE5PKdro0l+FnCpx1GDJaQ2WMcqm+ToXKMt8p8Uojk/MFRuGyz3V5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.12.tgz", + "integrity": "sha512-BMAlczRqC/LUt2P97E4apTBbkvS9JTJnp2DKFbCwpZ8vBvXVbNdqmvzW/OsdtI/+mGr+apkkpqGM8WecLkPgrA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.12.tgz", + "integrity": "sha512-zU5MyluNsykf5cOJ0LZZZjgAHbhPJ1cWfdH1ZXVMXxVMhEV0VZiZXQdwBBVvmvbF28EizeK7obG9fs+fpmS0eQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.12.tgz", + "integrity": "sha512-zUZMep7YONnp6954QOOwEBwFX9svlKd3ov6PkxKd53LGTHsp/gy7vHaPGhhjBmEpqXEXShi6dddjIkmd+NgMsA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.12.tgz", + "integrity": "sha512-ohqLPc7i67yunArPj1+/FeeJ7AgwAjHqKZ512ADk3WsE3FHU9l+m5aa7NdxXr0HmN1bjDlUslBjWNbFlD9y12Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.12.tgz", + "integrity": "sha512-GIIHtQXqgeOOqdG16a/A9N28GpkvjJnjYMhOnXVbn3EDJcoItdR58v/pGN31CHjyXDc8uCcRnFWmqaJt24AYJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.12.tgz", + "integrity": "sha512-zK0b9a1/0wZY+6FdOS3BpZcPc1kcx2G5yxxfEJtEUzVxI6n/FrC2Phsxj/YblPuBchhBZ/1wwn7AyEBUyNSa6g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.12.tgz", + "integrity": "sha512-y75OijvrBE/1XRrXq1jtrJfG26eHeMoqLJ2dwQNwviwTuTtHGCojsDO6BJNF8gU+3jTn1KzJEMETytwsFSvc+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.12.tgz", + "integrity": "sha512-JKgG8Q/LL/9sw/iHHxQyVMoQYu3rU3+a5Z87DxC+wAu3engz+EmctIrV+FGOgI6gWG1z1+5nDDbXiRMGQZXqiw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.12.tgz", + "integrity": "sha512-yoRIAqc0B4lDIAAEFEIu9ttTRFV84iuAl0KNCN6MhKLxNPfzwCBvEMgwco2f71GxmpBcTtn7KdErueZaM2rEvw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.12.tgz", + "integrity": "sha512-qYgt3dHPVvf/MgbIBpJ4Sup/yb9DAopZ3a2JgMpNKIHUpOdnJ2eHBo/aQdnd8dJ21X/+sS58wxHtA9lEazYtXQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.12.tgz", + "integrity": "sha512-wHphlMLK4ufNOONqukELfVIbnGQJrHJ/mxZMMrP2jYrPgCRZhOtf0kC4yAXBwnfmULimV1qt5UJJOw4Kh13Yfg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.12.tgz", + "integrity": "sha512-TeN//1Ft20ZZW41+zDSdOI/Os1bEq5dbvBvYkberB7PHABbRcsteeoNVZFlI0YLpGdlBqohEpjrn06kv8heCJg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.12.tgz", + "integrity": "sha512-AgUebVS4DoAblBgiB2ACQ/8l4eGE5aWBb8ZXtkXHiET9mbj7GuWt3OnsIW/zX+XHJt2RYJZctbQ2S/mDjbp0UA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.12.tgz", + "integrity": "sha512-dJ3Rb3Ei2u/ysSXd6pzleGtfDdc2MuzKt8qc6ls8vreP1G3B7HInX3i7gXS4BGeVd24pp0yqyS7bJ5NHaI9ing==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.12.tgz", + "integrity": "sha512-OrNJMGQbPaVyHHcDF8ybNSwu7TDOfX8NGpXCbetwOSP6txOJiWlgQnRymfC9ocR1S0Y5PW0Wb1mV6pUddqmvmQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.12.tgz", + "integrity": "sha512-55FzVCAiwE9FK8wWeCRuvjazNRJ1QqLCYGZVB6E8RuQuTeStSwotpSW4xoRGwp3a1wUsaVCdYcj5LGCASVJmMg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.12.tgz", + "integrity": "sha512-qnluf8rfb6Y5Lw2tirfK2quZOBbVqmwxut7GPCIJsM8lc4AEUj9L8y0YPdLaPK0TECt4IdyBdBD/KRFKorlK3g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.12.tgz", + "integrity": "sha512-+RkKpVQR7bICjTOPUpkTBTaJ4TFqQBX5Ywyd/HSdDkQGn65VPkTsR/pL4AMvuMWy+wnXgIl4EY6q4mVpJal8Kg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.12.tgz", + "integrity": "sha512-GNHuciv0mFM7ouzsU0+AwY+7eV4Mgo5WnbhfDCQGtpvOtD1vbOiRjPYG6dhmMoFyBjj+pNqQu2X+7DKn0KQ/Gw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.12.tgz", + "integrity": "sha512-kR8cezhYipbbypGkaqCTWIeu4zID17gamC8YTPXYtcN3E5BhhtTnwKBn9I0PJur/T6UVwIEGYzkffNL0lFvxEw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.12.tgz", + "integrity": "sha512-O0UYQVkvfM/jO8a4OwoV0mAKSJw+mjWTAd1MJd/1FCX6uiMdLmMRPK/w6e9OQ0ob2WGxzIm9va/KG0Ja4zIOgg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", + "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", + "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@hpcc-js/wasm": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@hpcc-js/wasm/-/wasm-2.13.0.tgz", + "integrity": "sha512-MvnUPnyMlN3/2IONCXwl/SBVWIfVOFJqvw+kFfI1QcwKjNmkwTAtG+9/m3nvofTymkASUUxNULbBmRDIr2uzIA==", + "dependencies": { + "yargs": "17.7.2" + }, + "bin": { + "dot-wasm": "bin/dot-wasm.js" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@tsconfig/strictest": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/strictest/-/strictest-2.0.1.tgz", + "integrity": "sha512-7JHHCbyCsGUxLd0pDbp24yz3zjxw2t673W5oAP6HCEdr/UUhaRhYd3SSnUsGCk+VnPVJVA4mXROzbhI+nyIk+w==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "16.11.68", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.68.tgz", + "integrity": "sha512-JkRpuVz3xCNCWaeQ5EHLR/6woMbHZz/jZ7Kmc63AkU+1HxnoUugzSWMck7dsR4DvNYX8jp9wTi9K7WvnxOIQZQ==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "node_modules/@types/vscode": { + "version": "1.78.1", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.78.1.tgz", + "integrity": "sha512-wEA+54axejHu7DhcUfnFBan1IqFD1gBDxAFz8LoX06NbNDMRJv/T6OGthOs52yZccasKfN588EyffHWABkR0fg==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.0.0.tgz", + "integrity": "sha512-xuv6ghKGoiq856Bww/yVYnXGsKa588kY3M0XK7uUW/3fJNNULKRfZfSBkMTSpqGG/8ZCXCadfh8G/z/B4aqS/A==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.0", + "@typescript-eslint/scope-manager": "6.0.0", + "@typescript-eslint/type-utils": "6.0.0", + "@typescript-eslint/utils": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.5.0", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.0.0.tgz", + "integrity": "sha512-TNaufYSPrr1U8n+3xN+Yp9g31vQDJqhXzzPSHfQDLcaO4tU+mCfODPxCwf4H530zo7aUBE3QIdxCXamEnG04Tg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.0.0", + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/typescript-estree": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.0.0.tgz", + "integrity": "sha512-o4q0KHlgCZTqjuaZ25nw5W57NeykZT9LiMEG4do/ovwvOcPnDO1BI5BQdCsUkjxFyrCL0cSzLjvIMfR9uo7cWg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.0.0.tgz", + "integrity": "sha512-ah6LJvLgkoZ/pyJ9GAdFkzeuMZ8goV6BH7eC9FPmojrnX9yNCIsfjB+zYcnex28YO3RFvBkV6rMV6WpIqkPvoQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.0.0", + "@typescript-eslint/utils": "6.0.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.0.0.tgz", + "integrity": "sha512-Zk9KDggyZM6tj0AJWYYKgF0yQyrcnievdhG0g5FqyU3Y2DRxJn4yWY21sJC0QKBckbsdKKjYDV2yVrrEvuTgxg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.0.0.tgz", + "integrity": "sha512-2zq4O7P6YCQADfmJ5OTDQTP3ktajnXIRrYAtHM9ofto/CJZV3QfJ89GEaM2BNGeSr1KgmBuLhEkz5FBkS2RQhQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.0", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.0.0.tgz", + "integrity": "sha512-SOr6l4NB6HE4H/ktz0JVVWNXqCJTOo/mHnvIte1ZhBQ0Cvd04x5uKZa3zT6tiodL06zf5xxdK8COiDvPnQ27JQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.3.0", + "@types/json-schema": "^7.0.11", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "6.0.0", + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/typescript-estree": "6.0.0", + "eslint-scope": "^5.1.1", + "semver": "^7.5.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.0.0.tgz", + "integrity": "sha512-cvJ63l8c0yXdeT5POHpL0Q1cZoRcmRKFCtSjNGJxPkcP571EfZMcNbzWAc7oK3D1dRzm/V5EwtkANTZxqvuuUA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.0.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vscode/test-electron": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.8.tgz", + "integrity": "sha512-b4aZZsBKtMGdDljAsOPObnAi7+VWIaYl3ylCz1jTs+oV6BZ4TNHcVNC3xUn0azPeszBmwSBDQYfFESIaUQnrOg==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "jszip": "^3.10.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@vscode/vsce": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.19.0.tgz", + "integrity": "sha512-dAlILxC5ggOutcvJY24jxz913wimGiUrHaPkk16Gm9/PGFbz1YezWtrXsTKUtJws4fIlpX2UIlVlVESWq8lkfQ==", + "dev": true, + "dependencies": { + "azure-devops-node-api": "^11.0.1", + "chalk": "^2.4.2", + "cheerio": "^1.0.0-rc.9", + "commander": "^6.1.0", + "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": "^5.1.0", + "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" + } + }, + "node_modules/@vscode/vsce/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@vscode/vsce/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@vscode/vsce/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@vscode/vsce/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/@vscode/vsce/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" + } + }, + "node_modules/@vscode/vsce/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" + } + }, + "node_modules/@vscode/vsce/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" + } + }, + "node_modules/@vscode/vsce/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, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@vscode/vsce/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@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" + } + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/anser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/anser/-/anser-2.1.1.tgz", + "integrity": "sha512-nqLm4HxOTpeLOxcmB3QWmV5TcDFhW9y/fyQ+hivtDFcK4OQ+pQ5fzPnXHM1Mfcm0VkLtvVi1TCPr++Qy0Q/3EQ==" + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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 + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "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/bl/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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 + }, + "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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": "*" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "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, + "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" + } + }, + "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, + "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" + } + }, + "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/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/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/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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/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" + } + }, + "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, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/d3": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", + "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-yEEyEAbDrF8C6Ob2myOBLjwBLck1Z89jMGFee0oPsn95GqjerpaOA4ch+vc2l0FNFFwMD5N7OCSEN5eAlsUbgQ==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", + "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-graphviz": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/d3-graphviz/-/d3-graphviz-5.0.2.tgz", + "integrity": "sha512-EVRow9rnFgm/L1trbbnu2PGOND11IcSEdWXbrDbz9hH0/Kj3YM2AqMkkTN/EAWgawD5/zryyCy+3Vm05oSJ1Kg==", + "dependencies": { + "@hpcc-js/wasm": "2.5.0", + "d3-dispatch": "^3.0.1", + "d3-format": "^3.1.0", + "d3-interpolate": "^3.0.1", + "d3-path": "^3.1.0", + "d3-timer": "^3.0.1", + "d3-transition": "^3.0.1", + "d3-zoom": "^3.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "d3-selection": "^3.0.0" + } + }, + "node_modules/d3-graphviz/node_modules/@hpcc-js/wasm": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@hpcc-js/wasm/-/wasm-2.5.0.tgz", + "integrity": "sha512-G26BamgaHW46f6P8bmkygapgNcy+tTDMwIvCzmMzdp39sxUS1u4gaT/vR2SSDc4x3SfL5RE4B2B8ef/wd429Hg==", + "dependencies": { + "yargs": "17.6.2" + }, + "bin": { + "dot-wasm": "bin/dot-wasm.js" + } + }, + "node_modules/d3-graphviz/node_modules/yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "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/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": 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" + } + }, + "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/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "dependencies": { + "robust-predicates": "^3.0.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "dev": true, + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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" + } + ] + }, + "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.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + }, + "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==" + }, + "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" + } + }, + "node_modules/entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.12.tgz", + "integrity": "sha512-XuOVLDdtsDslXStStduT41op21Ytmf4/BDS46aa3xPJ7X5h2eMWBF1oAe3QjUH3bDksocNXgzGUZ7XHIBya6Tg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.12", + "@esbuild/android-arm64": "0.18.12", + "@esbuild/android-x64": "0.18.12", + "@esbuild/darwin-arm64": "0.18.12", + "@esbuild/darwin-x64": "0.18.12", + "@esbuild/freebsd-arm64": "0.18.12", + "@esbuild/freebsd-x64": "0.18.12", + "@esbuild/linux-arm": "0.18.12", + "@esbuild/linux-arm64": "0.18.12", + "@esbuild/linux-ia32": "0.18.12", + "@esbuild/linux-loong64": "0.18.12", + "@esbuild/linux-mips64el": "0.18.12", + "@esbuild/linux-ppc64": "0.18.12", + "@esbuild/linux-riscv64": "0.18.12", + "@esbuild/linux-s390x": "0.18.12", + "@esbuild/linux-x64": "0.18.12", + "@esbuild/netbsd-x64": "0.18.12", + "@esbuild/openbsd-x64": "0.18.12", + "@esbuild/sunos-x64": "0.18.12", + "@esbuild/win32-arm64": "0.18.12", + "@esbuild/win32-ia32": "0.18.12", + "@esbuild/win32-x64": "0.18.12" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", + "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.1.0", + "@eslint/js": "8.44.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.6.0", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/espree": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", + "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": 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 + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "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==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "optional": true + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "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.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "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 + }, + "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, + "optional": true + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "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 + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "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" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "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" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "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" + } + }, + "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, + "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" + } + }, + "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, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "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/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "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" + } + }, + "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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "optional": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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 + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "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/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/node-abi": { + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.31.0.tgz", + "integrity": "sha512-eSKV6s+APenqVh8ubJyiu/YhZgxQpGP66ntzUb3lY1xB9ukSRaGnx0AIxI+IM+1+IVYC1oWobgG5L3Lt9ARykQ==", + "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.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ovsx": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.8.2.tgz", + "integrity": "sha512-btDXZorXlmwN9+9Un3khrVygCXmhwbrtg8gifNXw92rZPXcRBAiLG/L09Kb6srhGEratsFt42AktfD8t9XhzoA==", + "dev": true, + "dependencies": { + "@vscode/vsce": "^2.19.0", + "commander": "^6.1.0", + "follow-redirects": "^1.14.6", + "is-ci": "^2.0.0", + "leven": "^3.1.0", + "semver": "^7.5.2", + "tmp": "^0.2.1" + }, + "bin": { + "ovsx": "lib/ovsx" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ovsx/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" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-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, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "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" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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 + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "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/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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" + } + ] + }, + "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" + } + }, + "node_modules/rc/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" + } + }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "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/robust-predicates": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", + "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "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" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "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/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/tar-stream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "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/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", + "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==", + "dev": true + }, + "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/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.9.tgz", + "integrity": "sha512-uSmjE38B80wjL85UFX3sTYEUlvZ1JgCRhsWj/fJ4rZ0FqDUFoIuodtiVeE+cUqiVTOKPdKrp/sdftD15MDek6g==", + "dev": true, + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "node_modules/typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "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/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "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 + }, + "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 + }, + "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==", + "engines": { + "node": ">=14.0.0" + } + }, + "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==", + "dependencies": { + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.3" + }, + "engines": { + "vscode": "^1.67.0" + } + }, + "node_modules/vscode-languageclient/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": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/vscode-languageclient/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/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==", + "dependencies": { + "vscode-jsonrpc": "8.1.0", + "vscode-languageserver-types": "3.17.3" + } + }, + "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==" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/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==", + "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" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "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/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/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, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "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, + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, + "node_modules/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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json new file mode 100644 index 00000000000..352eb242293 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -0,0 +1,2295 @@ +{ + "name": "rust-analyzer", + "displayName": "rust-analyzer", + "description": "Rust language support for Visual Studio Code", + "private": true, + "icon": "icon.png", + "version": "0.5.0-dev", + "releaseTag": null, + "publisher": "rust-lang", + "repository": { + "url": "https://github.com/rust-lang/rust-analyzer.git", + "type": "git" + }, + "homepage": "https://rust-analyzer.github.io/", + "license": "MIT OR Apache-2.0", + "keywords": [ + "rust" + ], + "categories": [ + "Formatters", + "Programming Languages" + ], + "capabilities": { + "untrustedWorkspaces": { + "supported": false, + "description": "rust-analyzer invokes binaries set up by its configuration as well as the Rust toolchain's binaries. A malicious actor could exploit this to run arbitrary code on your machine." + } + }, + "engines": { + "vscode": "^1.78.0" + }, + "enabledApiProposals": [], + "scripts": { + "vscode:prepublish": "npm run build-base -- --minify", + "package": "vsce package -o rust-analyzer.vsix", + "build-base": "esbuild ./src/main.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node --target=node16", + "build": "npm run build-base -- --sourcemap", + "watch": "npm run build-base -- --sourcemap --watch", + "format": "prettier --write .", + "format:check": "prettier --check .", + "lint": "eslint -c .eslintrc.js --ext ts ./src ./tests", + "lint:fix": "npm run lint -- --fix", + "typecheck": "tsc", + "pretest": "npm run typecheck && npm run build", + "test": "node ./out/tests/runTests.js" + }, + "dependencies": { + "@hpcc-js/wasm": "^2.13.0", + "anser": "^2.1.1", + "d3": "^7.8.5", + "d3-graphviz": "^5.0.2", + "vscode-languageclient": "^8.1.0" + }, + "devDependencies": { + "@tsconfig/strictest": "^2.0.1", + "@types/node": "~16.11.7", + "@types/vscode": "~1.78.1", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vscode/test-electron": "^2.3.8", + "@vscode/vsce": "^2.19.0", + "esbuild": "^0.18.12", + "eslint": "^8.44.0", + "eslint-config-prettier": "^8.8.0", + "ovsx": "^0.8.2", + "prettier": "^3.0.0", + "tslib": "^2.6.0", + "typescript": "^5.1.6" + }, + "activationEvents": [ + "workspaceContains:Cargo.toml", + "workspaceContains:*/Cargo.toml", + "workspaceContains:rust-project.json", + "workspaceContains:*/rust-project.json" + ], + "main": "./out/main", + "contributes": { + "taskDefinitions": [ + { + "type": "cargo", + "required": [ + "command" + ], + "properties": { + "label": { + "type": "string" + }, + "command": { + "type": "string" + }, + "args": { + "type": "array", + "items": { + "type": "string" + } + }, + "env": { + "type": "object", + "patternProperties": { + ".+": { + "type": "string" + } + } + } + } + } + ], + "commands": [ + { + "command": "rust-analyzer.syntaxTree", + "title": "Show Syntax Tree", + "category": "rust-analyzer (debug command)" + }, + { + "command": "rust-analyzer.viewHir", + "title": "View Hir", + "category": "rust-analyzer (debug command)" + }, + { + "command": "rust-analyzer.viewMir", + "title": "View Mir", + "category": "rust-analyzer (debug command)" + }, + { + "command": "rust-analyzer.interpretFunction", + "title": "Interpret Function", + "category": "rust-analyzer (debug command)" + }, + { + "command": "rust-analyzer.viewFileText", + "title": "View File Text (as seen by the server)", + "category": "rust-analyzer (debug command)" + }, + { + "command": "rust-analyzer.viewItemTree", + "title": "Debug ItemTree", + "category": "rust-analyzer (debug command)" + }, + { + "command": "rust-analyzer.shuffleCrateGraph", + "title": "Shuffle Crate Graph", + "category": "rust-analyzer (debug command)" + }, + { + "command": "rust-analyzer.memoryUsage", + "title": "Memory Usage (Clears Database)", + "category": "rust-analyzer (debug command)" + }, + { + "command": "rust-analyzer.viewCrateGraph", + "title": "View Crate Graph", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.viewFullCrateGraph", + "title": "View Crate Graph (Full)", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.expandMacro", + "title": "Expand macro recursively at caret", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.matchingBrace", + "title": "Find matching brace", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.parentModule", + "title": "Locate parent module", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.joinLines", + "title": "Join lines", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.run", + "title": "Run", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.copyRunCommandLine", + "title": "Copy Run Command Line", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.debug", + "title": "Debug", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.newDebugConfig", + "title": "Generate launch configuration", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.analyzerStatus", + "title": "Status", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.reloadWorkspace", + "title": "Reload workspace", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.rebuildProcMacros", + "title": "Rebuild proc macros and build scripts", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.restartServer", + "title": "Restart server", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.startServer", + "title": "Start server", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.stopServer", + "title": "Stop server", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.onEnter", + "title": "Enhanced enter key", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.ssr", + "title": "Structural Search Replace", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.serverVersion", + "title": "Show RA Version", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.openDocs", + "title": "Open Docs", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.openExternalDocs", + "title": "Open External Docs", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.openCargoToml", + "title": "Open Cargo.toml", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.peekTests", + "title": "Peek Related Tests", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.moveItemUp", + "title": "Move Item Up", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.moveItemDown", + "title": "Move Item Down", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.cancelFlycheck", + "title": "Cancel running flychecks", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.runFlycheck", + "title": "Run flycheck", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.clearFlycheck", + "title": "Clear flycheck diagnostics", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.revealDependency", + "title": "Reveal File", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.viewMemoryLayout", + "title": "View Memory Layout", + "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.toggleCheckOnSave", + "title": "Toggle Check on Save", + "category": "rust-analyzer" + } + ], + "keybindings": [ + { + "command": "rust-analyzer.parentModule", + "key": "ctrl+shift+u", + "when": "editorTextFocus && editorLangId == rust" + }, + { + "command": "rust-analyzer.joinLines", + "key": "ctrl+shift+j", + "when": "editorTextFocus && editorLangId == rust" + } + ], + "configuration": { + "type": "object", + "title": "rust-analyzer", + "properties": { + "rust-analyzer.cargoRunner": { + "type": [ + "null", + "string" + ], + "default": null, + "description": "Custom cargo runner extension ID." + }, + "rust-analyzer.runnables.extraEnv": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "array", + "items": { + "type": "object", + "properties": { + "platform": { + "type": [ + "null", + "string", + "array" + ], + "default": null, + "markdownDescription": "Platform(s) filter like \"win32\" or [\"linux\", \"win32\"]. See [process.platform](https://nodejs.org/api/process.html#processplatform) values." + }, + "mask": { + "type": "string", + "description": "Runnable name mask" + }, + "env": { + "type": "object", + "description": "Variables in form of { \"key\": \"value\"}" + } + } + } + }, + { + "type": "object", + "description": "Variables in form of { \"key\": \"value\"}" + } + ], + "default": null, + "markdownDescription": "Environment variables passed to the runnable launched using `Test` or `Debug` lens or `rust-analyzer.run` command." + }, + "rust-analyzer.runnables.problemMatcher": { + "type": "array", + "items": { + "type": "string" + }, + "default": [ + "$rustc" + ], + "markdownDescription": "Problem matchers to use for `rust-analyzer.run` command, eg `[\"$rustc\", \"$rust-panic\"]`." + }, + "rust-analyzer.statusBar.clickAction": { + "type": "string", + "enum": [ + "stopServer", + "openLogs" + ], + "enumDescriptions": [ + "Stop Server", + "Open Logs" + ], + "default": "openLogs", + "markdownDescription": "Action to run when clicking the extension status bar item." + }, + "rust-analyzer.server.path": { + "type": [ + "null", + "string" + ], + "scope": "machine-overridable", + "default": null, + "markdownDescription": "Path to rust-analyzer executable (points to bundled binary by default)." + }, + "rust-analyzer.server.extraEnv": { + "type": [ + "null", + "object" + ], + "additionalProperties": { + "type": [ + "string", + "number" + ] + }, + "default": null, + "markdownDescription": "Extra environment variables that will be passed to the rust-analyzer executable. Useful for passing e.g. `RA_LOG` for debugging." + }, + "rust-analyzer.trace.server": { + "type": "string", + "scope": "window", + "enum": [ + "off", + "messages", + "verbose" + ], + "enumDescriptions": [ + "No traces", + "Error only", + "Full log" + ], + "default": "off", + "description": "Trace requests to the rust-analyzer (this is usually overly verbose and not recommended for regular users)." + }, + "rust-analyzer.trace.extension": { + "description": "Enable logging of VS Code extensions itself.", + "type": "boolean", + "default": false + }, + "rust-analyzer.debug.engine": { + "type": "string", + "enum": [ + "auto", + "vadimcn.vscode-lldb", + "ms-vscode.cpptools" + ], + "default": "auto", + "description": "Preferred debug engine.", + "markdownEnumDescriptions": [ + "First try to use [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb), if it's not installed try to use [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools).", + "Use [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)", + "Use [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools)" + ] + }, + "rust-analyzer.debug.sourceFileMap": { + "type": [ + "object", + "string" + ], + "const": "auto", + "description": "Optional source file mappings passed to the debug engine.", + "default": { + "/rustc/<id>": "${env:USERPROFILE}/.rustup/toolchains/<toolchain-id>/lib/rustlib/src/rust" + } + }, + "rust-analyzer.debug.openDebugPane": { + "markdownDescription": "Whether to open up the `Debug Panel` on debugging start.", + "type": "boolean", + "default": false + }, + "rust-analyzer.debug.engineSettings": { + "type": "object", + "default": {}, + "markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`" + }, + "rust-analyzer.restartServerOnConfigChange": { + "markdownDescription": "Whether to restart the server automatically when certain settings that require a restart are changed.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.typing.continueCommentsOnNewline": { + "markdownDescription": "Whether to prefix newlines after comments with the corresponding comment prefix.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.diagnostics.previewRustcOutput": { + "markdownDescription": "Whether to show the main part of the rendered rustc output of a diagnostic message.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.diagnostics.useRustcErrorCode": { + "markdownDescription": "Whether to show diagnostics using the original rustc error code. If this is false, all rustc diagnostics will have the code 'rustc(Click for full compiler diagnostics)'", + "default": false, + "type": "boolean" + }, + "rust-analyzer.discoverProjectRunner": { + "markdownDescription": "Sets the extension responsible for determining which extension the rust-analyzer extension uses to generate `rust-project.json` files. This should should only be used\n if a build system like Buck or Bazel is also in use.", + "default": null, + "type": [ + "null", + "string" + ] + }, + "rust-analyzer.showUnlinkedFileNotification": { + "markdownDescription": "Whether to show a notification for unlinked files asking the user to add the corresponding Cargo.toml to the linked projects setting.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.showRequestFailedErrorNotification": { + "markdownDescription": "Whether to show error notifications for failing requests.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.showDependenciesExplorer": { + "markdownDescription": "Whether to show the dependencies view.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.testExplorer": { + "markdownDescription": "Whether to show the test explorer.", + "default": false, + "type": "boolean" + }, + "$generated-start": {}, + "rust-analyzer.assist.emitMustUse": { + "markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.assist.expressionFillDefault": { + "markdownDescription": "Placeholder expression to use for missing expressions in assists.", + "default": "todo", + "type": "string", + "enum": [ + "todo", + "default" + ], + "enumDescriptions": [ + "Fill missing expressions with the `todo` macro", + "Fill missing expressions with reasonable defaults, `new` or `default` constructors." + ] + }, + "rust-analyzer.cachePriming.enable": { + "markdownDescription": "Warm up caches on project load.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.cachePriming.numThreads": { + "markdownDescription": "How many worker threads to handle priming caches. The default `0` means to pick automatically.", + "default": 0, + "type": "number", + "minimum": 0, + "maximum": 255 + }, + "rust-analyzer.cargo.allTargets": { + "markdownDescription": "Pass `--all-targets` to cargo invocation.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.cargo.autoreload": { + "markdownDescription": "Automatically refresh project info via `cargo metadata` on\n`Cargo.toml` or `.cargo/config.toml` changes.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.cargo.buildScripts.enable": { + "markdownDescription": "Run build scripts (`build.rs`) for more precise code analysis.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.cargo.buildScripts.invocationLocation": { + "markdownDescription": "Specifies the working directory for running build scripts.\n- \"workspace\": run build scripts for a workspace in the workspace's root directory.\n This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`.\n- \"root\": run build scripts in the project's root directory.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", + "default": "workspace", + "type": "string", + "enum": [ + "workspace", + "root" + ], + "enumDescriptions": [ + "The command will be executed in the corresponding workspace root.", + "The command will be executed in the project root." + ] + }, + "rust-analyzer.cargo.buildScripts.invocationStrategy": { + "markdownDescription": "Specifies the invocation strategy to use when running the build scripts command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", + "default": "per_workspace", + "type": "string", + "enum": [ + "per_workspace", + "once" + ], + "enumDescriptions": [ + "The command will be executed for each workspace.", + "The command will be executed once." + ] + }, + "rust-analyzer.cargo.buildScripts.overrideCommand": { + "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#` and\n`#rust-analyzer.cargo.buildScripts.invocationLocation#`.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets\n```\n.", + "default": null, + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + } + }, + "rust-analyzer.cargo.buildScripts.rebuildOnSave": { + "markdownDescription": "Rerun proc-macros building/build-scripts running when proc-macro\nor build-script sources change and are saved.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.cargo.buildScripts.useRustcWrapper": { + "markdownDescription": "Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to\navoid checking unnecessary things.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.cargo.cfgs": { + "markdownDescription": "List of cfg options to enable with the given values.", + "default": { + "debug_assertions": null, + "miri": null + }, + "type": "object" + }, + "rust-analyzer.cargo.extraArgs": { + "markdownDescription": "Extra arguments that are passed to every cargo invocation.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "rust-analyzer.cargo.extraEnv": { + "markdownDescription": "Extra environment variables that will be set when running cargo, rustc\nor other commands within the workspace. Useful for setting RUSTFLAGS.", + "default": {}, + "type": "object" + }, + "rust-analyzer.cargo.features": { + "markdownDescription": "List of features to activate.\n\nSet this to `\"all\"` to pass `--all-features` to cargo.", + "default": [], + "anyOf": [ + { + "type": "string", + "enum": [ + "all" + ], + "enumDescriptions": [ + "Pass `--all-features` to cargo" + ] + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "rust-analyzer.cargo.noDefaultFeatures": { + "markdownDescription": "Whether to pass `--no-default-features` to cargo.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.cargo.sysroot": { + "markdownDescription": "Relative path to the sysroot, or \"discover\" to try to automatically find it via\n\"rustc --print sysroot\".\n\nUnsetting this disables sysroot loading.\n\nThis option does not take effect until rust-analyzer is restarted.", + "default": "discover", + "type": [ + "null", + "string" + ] + }, + "rust-analyzer.cargo.sysrootQueryMetadata": { + "markdownDescription": "Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze\nthird-party dependencies of the standard libraries.\n\nThis will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer\nwill attempt to clean up afterwards, but nevertheless requires the location to be\nwritable to.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.cargo.sysrootSrc": { + "markdownDescription": "Relative path to the sysroot library sources. If left unset, this will default to\n`{cargo.sysroot}/lib/rustlib/src/rust/library`.\n\nThis option does not take effect until rust-analyzer is restarted.", + "default": null, + "type": [ + "null", + "string" + ] + }, + "rust-analyzer.cargo.target": { + "markdownDescription": "Compilation target override (target triple).", + "default": null, + "type": [ + "null", + "string" + ] + }, + "rust-analyzer.cargo.targetDir": { + "markdownDescription": "Optional path to a rust-analyzer specific target directory.\nThis prevents rust-analyzer's `cargo check` and initial build-script and proc-macro\nbuilding from locking the `Cargo.lock` at the expense of duplicating build artifacts.\n\nSet to `true` to use a subdirectory of the existing target directory or\nset to a path relative to the workspace to use that path.", + "default": null, + "anyOf": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "rust-analyzer.checkOnSave": { + "markdownDescription": "Run the check command for diagnostics on save.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.check.allTargets": { + "markdownDescription": "Check all targets and tests (`--all-targets`). Defaults to\n`#rust-analyzer.cargo.allTargets#`.", + "default": null, + "type": [ + "null", + "boolean" + ] + }, + "rust-analyzer.check.command": { + "markdownDescription": "Cargo command to use for `cargo check`.", + "default": "check", + "type": "string" + }, + "rust-analyzer.check.extraArgs": { + "markdownDescription": "Extra arguments for `cargo check`.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "rust-analyzer.check.extraEnv": { + "markdownDescription": "Extra environment variables that will be set when running `cargo check`.\nExtends `#rust-analyzer.cargo.extraEnv#`.", + "default": {}, + "type": "object" + }, + "rust-analyzer.check.features": { + "markdownDescription": "List of features to activate. Defaults to\n`#rust-analyzer.cargo.features#`.\n\nSet to `\"all\"` to pass `--all-features` to Cargo.", + "default": null, + "anyOf": [ + { + "type": "string", + "enum": [ + "all" + ], + "enumDescriptions": [ + "Pass `--all-features` to cargo" + ] + }, + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "rust-analyzer.check.ignore": { + "markdownDescription": "List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.\n\nFor example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...", + "default": [], + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "rust-analyzer.check.invocationLocation": { + "markdownDescription": "Specifies the working directory for running checks.\n- \"workspace\": run checks for workspaces in the corresponding workspaces' root directories.\n This falls back to \"root\" if `#rust-analyzer.check.invocationStrategy#` is set to `once`.\n- \"root\": run checks in the project's root directory.\nThis config only has an effect when `#rust-analyzer.check.overrideCommand#`\nis set.", + "default": "workspace", + "type": "string", + "enum": [ + "workspace", + "root" + ], + "enumDescriptions": [ + "The command will be executed in the corresponding workspace root.", + "The command will be executed in the project root." + ] + }, + "rust-analyzer.check.invocationStrategy": { + "markdownDescription": "Specifies the invocation strategy to use when running the check command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.check.overrideCommand#`\nis set.", + "default": "per_workspace", + "type": "string", + "enum": [ + "per_workspace", + "once" + ], + "enumDescriptions": [ + "The command will be executed for each workspace.", + "The command will be executed once." + ] + }, + "rust-analyzer.check.noDefaultFeatures": { + "markdownDescription": "Whether to pass `--no-default-features` to Cargo. Defaults to\n`#rust-analyzer.cargo.noDefaultFeatures#`.", + "default": null, + "type": [ + "null", + "boolean" + ] + }, + "rust-analyzer.check.overrideCommand": { + "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#` and\n`#rust-analyzer.check.invocationLocation#`.\n\nIf `$saved_file` is part of the command, rust-analyzer will pass\nthe absolute path of the saved file to the provided command. This is\nintended to be used with non-Cargo build systems.\nNote that `$saved_file` is experimental and may be removed in the futureg.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.", + "default": null, + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + } + }, + "rust-analyzer.check.targets": { + "markdownDescription": "Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.\n\nCan be a single target, e.g. `\"x86_64-unknown-linux-gnu\"` or a list of targets, e.g.\n`[\"aarch64-apple-darwin\", \"x86_64-apple-darwin\"]`.\n\nAliased as `\"checkOnSave.targets\"`.", + "default": null, + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "rust-analyzer.check.workspace": { + "markdownDescription": "Whether `--workspace` should be passed to `cargo check`.\nIf false, `-p <package>` will be passed instead.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.completion.autoimport.enable": { + "markdownDescription": "Toggles the additional completions that automatically add imports when completed.\nNote that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.completion.autoself.enable": { + "markdownDescription": "Toggles the additional completions that automatically show method calls and field accesses\nwith `self` prefixed to them when inside a method.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.completion.callable.snippets": { + "markdownDescription": "Whether to add parenthesis and argument snippets when completing function.", + "default": "fill_arguments", + "type": "string", + "enum": [ + "fill_arguments", + "add_parentheses", + "none" + ], + "enumDescriptions": [ + "Add call parentheses and pre-fill arguments.", + "Add call parentheses.", + "Do no snippet completions for callables." + ] + }, + "rust-analyzer.completion.fullFunctionSignatures.enable": { + "markdownDescription": "Whether to show full function/method signatures in completion docs.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.completion.limit": { + "markdownDescription": "Maximum number of completions to return. If `None`, the limit is infinite.", + "default": null, + "type": [ + "null", + "integer" + ], + "minimum": 0 + }, + "rust-analyzer.completion.postfix.enable": { + "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.completion.privateEditable.enable": { + "markdownDescription": "Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.completion.snippets.custom": { + "markdownDescription": "Custom completion snippets.", + "default": { + "Arc::new": { + "postfix": "arc", + "body": "Arc::new(${receiver})", + "requires": "std::sync::Arc", + "description": "Put the expression into an `Arc`", + "scope": "expr" + }, + "Rc::new": { + "postfix": "rc", + "body": "Rc::new(${receiver})", + "requires": "std::rc::Rc", + "description": "Put the expression into an `Rc`", + "scope": "expr" + }, + "Box::pin": { + "postfix": "pinbox", + "body": "Box::pin(${receiver})", + "requires": "std::boxed::Box", + "description": "Put the expression into a pinned `Box`", + "scope": "expr" + }, + "Ok": { + "postfix": "ok", + "body": "Ok(${receiver})", + "description": "Wrap the expression in a `Result::Ok`", + "scope": "expr" + }, + "Err": { + "postfix": "err", + "body": "Err(${receiver})", + "description": "Wrap the expression in a `Result::Err`", + "scope": "expr" + }, + "Some": { + "postfix": "some", + "body": "Some(${receiver})", + "description": "Wrap the expression in an `Option::Some`", + "scope": "expr" + } + }, + "type": "object" + }, + "rust-analyzer.completion.termSearch.enable": { + "markdownDescription": "Whether to enable term search based snippets like `Some(foo.bar().baz())`.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.diagnostics.disabled": { + "markdownDescription": "List of rust-analyzer diagnostics to disable.", + "default": [], + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "rust-analyzer.diagnostics.enable": { + "markdownDescription": "Whether to show native rust-analyzer diagnostics.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.diagnostics.experimental.enable": { + "markdownDescription": "Whether to show experimental rust-analyzer diagnostics that might\nhave more false positives than usual.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.diagnostics.remapPrefix": { + "markdownDescription": "Map of prefixes to be substituted when parsing diagnostic file paths.\nThis should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.", + "default": {}, + "type": "object" + }, + "rust-analyzer.diagnostics.styleLints.enable": { + "markdownDescription": "Whether to run additional style lints.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.diagnostics.warningsAsHint": { + "markdownDescription": "List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code\nand will not show up in the `Problems Panel`.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "rust-analyzer.diagnostics.warningsAsInfo": { + "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "rust-analyzer.files.excludeDirs": { + "markdownDescription": "These directories will be ignored by rust-analyzer. They are\nrelative to the workspace root, and globs are not supported. You may\nalso need to add the folders to Code's `files.watcherExclude`.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "rust-analyzer.files.watcher": { + "markdownDescription": "Controls file watching implementation.", + "default": "client", + "type": "string", + "enum": [ + "client", + "server" + ], + "enumDescriptions": [ + "Use the client (editor) to watch files for changes", + "Use server-side file watching" + ] + }, + "rust-analyzer.highlightRelated.breakPoints.enable": { + "markdownDescription": "Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.highlightRelated.closureCaptures.enable": { + "markdownDescription": "Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.highlightRelated.exitPoints.enable": { + "markdownDescription": "Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).", + "default": true, + "type": "boolean" + }, + "rust-analyzer.highlightRelated.references.enable": { + "markdownDescription": "Enables highlighting of related references while the cursor is on any identifier.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.highlightRelated.yieldPoints.enable": { + "markdownDescription": "Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.hover.actions.debug.enable": { + "markdownDescription": "Whether to show `Debug` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.hover.actions.enable": { + "markdownDescription": "Whether to show HoverActions in Rust files.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.hover.actions.gotoTypeDef.enable": { + "markdownDescription": "Whether to show `Go to Type Definition` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.hover.actions.implementations.enable": { + "markdownDescription": "Whether to show `Implementations` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.hover.actions.references.enable": { + "markdownDescription": "Whether to show `References` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.hover.actions.run.enable": { + "markdownDescription": "Whether to show `Run` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.hover.documentation.enable": { + "markdownDescription": "Whether to show documentation on hover.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.hover.documentation.keywords.enable": { + "markdownDescription": "Whether to show keyword hover popups. Only applies when\n`#rust-analyzer.hover.documentation.enable#` is set.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.hover.links.enable": { + "markdownDescription": "Use markdown syntax for links on hover.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.hover.memoryLayout.alignment": { + "markdownDescription": "How to render the align information in a memory layout hover.", + "default": "hexadecimal", + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": [ + "both", + "decimal", + "hexadecimal" + ], + "enumDescriptions": [ + "Render as 12 (0xC)", + "Render as 12", + "Render as 0xC" + ] + } + ] + }, + "rust-analyzer.hover.memoryLayout.enable": { + "markdownDescription": "Whether to show memory layout data on hover.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.hover.memoryLayout.niches": { + "markdownDescription": "How to render the niche information in a memory layout hover.", + "default": false, + "type": [ + "null", + "boolean" + ] + }, + "rust-analyzer.hover.memoryLayout.offset": { + "markdownDescription": "How to render the offset information in a memory layout hover.", + "default": "hexadecimal", + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": [ + "both", + "decimal", + "hexadecimal" + ], + "enumDescriptions": [ + "Render as 12 (0xC)", + "Render as 12", + "Render as 0xC" + ] + } + ] + }, + "rust-analyzer.hover.memoryLayout.size": { + "markdownDescription": "How to render the size information in a memory layout hover.", + "default": "both", + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": [ + "both", + "decimal", + "hexadecimal" + ], + "enumDescriptions": [ + "Render as 12 (0xC)", + "Render as 12", + "Render as 0xC" + ] + } + ] + }, + "rust-analyzer.hover.show.structFields": { + "markdownDescription": "How many fields of a struct to display when hovering a struct.", + "default": null, + "type": [ + "null", + "integer" + ], + "minimum": 0 + }, + "rust-analyzer.hover.show.traitAssocItems": { + "markdownDescription": "How many associated items of a trait to display when hovering a trait.", + "default": null, + "type": [ + "null", + "integer" + ], + "minimum": 0 + }, + "rust-analyzer.imports.granularity.enforce": { + "markdownDescription": "Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.imports.granularity.group": { + "markdownDescription": "How imports should be grouped into use statements.", + "default": "crate", + "type": "string", + "enum": [ + "preserve", + "crate", + "module", + "item", + "one" + ], + "enumDescriptions": [ + "Do not change the granularity of any imports and preserve the original structure written by the developer.", + "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.", + "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.", + "Flatten imports so that each has its own use statement.", + "Merge all imports into a single use statement as long as they have the same visibility and attributes." + ] + }, + "rust-analyzer.imports.group.enable": { + "markdownDescription": "Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.imports.merge.glob": { + "markdownDescription": "Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.imports.preferNoStd": { + "markdownDescription": "Prefer to unconditionally use imports of the core and alloc crate, over the std crate.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.imports.preferPrelude": { + "markdownDescription": "Whether to prefer import paths containing a `prelude` module.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.imports.prefix": { + "markdownDescription": "The path structure for newly inserted paths to use.", + "default": "plain", + "type": "string", + "enum": [ + "plain", + "self", + "crate" + ], + "enumDescriptions": [ + "Insert import paths relative to the current module, using up to one `super` prefix if the parent module contains the requested item.", + "Insert import paths relative to the current module, using up to one `super` prefix if the parent module contains the requested item. Prefixes `self` in front of the path if it starts with a module.", + "Force import paths to be absolute by always starting them with `crate` or the extern crate name they come from." + ] + }, + "rust-analyzer.inlayHints.bindingModeHints.enable": { + "markdownDescription": "Whether to show inlay type hints for binding modes.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.inlayHints.chainingHints.enable": { + "markdownDescription": "Whether to show inlay type hints for method chains.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.inlayHints.closingBraceHints.enable": { + "markdownDescription": "Whether to show inlay hints after a closing `}` to indicate what item it belongs to.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.inlayHints.closingBraceHints.minLines": { + "markdownDescription": "Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1\nto always show them).", + "default": 25, + "type": "integer", + "minimum": 0 + }, + "rust-analyzer.inlayHints.closureCaptureHints.enable": { + "markdownDescription": "Whether to show inlay hints for closure captures.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.inlayHints.closureReturnTypeHints.enable": { + "markdownDescription": "Whether to show inlay type hints for return types of closures.", + "default": "never", + "type": "string", + "enum": [ + "always", + "never", + "with_block" + ], + "enumDescriptions": [ + "Always show type hints for return types of closures.", + "Never show type hints for return types of closures.", + "Only show type hints for return types of closures with blocks." + ] + }, + "rust-analyzer.inlayHints.closureStyle": { + "markdownDescription": "Closure notation in type and chaining inlay hints.", + "default": "impl_fn", + "type": "string", + "enum": [ + "impl_fn", + "rust_analyzer", + "with_id", + "hide" + ], + "enumDescriptions": [ + "`impl_fn`: `impl FnMut(i32, u64) -> i8`", + "`rust_analyzer`: `|i32, u64| -> i8`", + "`with_id`: `{closure#14352}`, where that id is the unique number of the closure in r-a internals", + "`hide`: Shows `...` for every closure type" + ] + }, + "rust-analyzer.inlayHints.discriminantHints.enable": { + "markdownDescription": "Whether to show enum variant discriminant hints.", + "default": "never", + "type": "string", + "enum": [ + "always", + "never", + "fieldless" + ], + "enumDescriptions": [ + "Always show all discriminant hints.", + "Never show discriminant hints.", + "Only show discriminant hints on fieldless enum variants." + ] + }, + "rust-analyzer.inlayHints.expressionAdjustmentHints.enable": { + "markdownDescription": "Whether to show inlay hints for type adjustments.", + "default": "never", + "type": "string", + "enum": [ + "always", + "never", + "reborrow" + ], + "enumDescriptions": [ + "Always show all adjustment hints.", + "Never show adjustment hints.", + "Only show auto borrow and dereference adjustment hints." + ] + }, + "rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe": { + "markdownDescription": "Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.inlayHints.expressionAdjustmentHints.mode": { + "markdownDescription": "Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).", + "default": "prefix", + "type": "string", + "enum": [ + "prefix", + "postfix", + "prefer_prefix", + "prefer_postfix" + ], + "enumDescriptions": [ + "Always show adjustment hints as prefix (`*expr`).", + "Always show adjustment hints as postfix (`expr.*`).", + "Show prefix or postfix depending on which uses less parenthesis, preferring prefix.", + "Show prefix or postfix depending on which uses less parenthesis, preferring postfix." + ] + }, + "rust-analyzer.inlayHints.implicitDrops.enable": { + "markdownDescription": "Whether to show implicit drop hints.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.inlayHints.lifetimeElisionHints.enable": { + "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.", + "default": "never", + "type": "string", + "enum": [ + "always", + "never", + "skip_trivial" + ], + "enumDescriptions": [ + "Always show lifetime elision hints.", + "Never show lifetime elision hints.", + "Only show lifetime elision hints if a return type is involved." + ] + }, + "rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames": { + "markdownDescription": "Whether to prefer using parameter names as the name for elided lifetime hints if possible.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.inlayHints.maxLength": { + "markdownDescription": "Maximum length for inlay hints. Set to null to have an unlimited length.", + "default": 25, + "type": [ + "null", + "integer" + ], + "minimum": 0 + }, + "rust-analyzer.inlayHints.parameterHints.enable": { + "markdownDescription": "Whether to show function parameter name inlay hints at the call\nsite.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.inlayHints.rangeExclusiveHints.enable": { + "markdownDescription": "Whether to show exclusive range inlay hints.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.inlayHints.reborrowHints.enable": { + "markdownDescription": "Whether to show inlay hints for compiler inserted reborrows.\nThis setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#.", + "default": "never", + "type": "string", + "enum": [ + "always", + "never", + "mutable" + ], + "enumDescriptions": [ + "Always show reborrow hints.", + "Never show reborrow hints.", + "Only show mutable reborrow hints." + ] + }, + "rust-analyzer.inlayHints.renderColons": { + "markdownDescription": "Whether to render leading colons for type hints, and trailing colons for parameter hints.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.inlayHints.typeHints.enable": { + "markdownDescription": "Whether to show inlay type hints for variables.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.inlayHints.typeHints.hideClosureInitialization": { + "markdownDescription": "Whether to hide inlay type hints for `let` statements that initialize to a closure.\nOnly applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.inlayHints.typeHints.hideNamedConstructor": { + "markdownDescription": "Whether to hide inlay type hints for constructors.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.interpret.tests": { + "markdownDescription": "Enables the experimental support for interpreting tests.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.joinLines.joinAssignments": { + "markdownDescription": "Join lines merges consecutive declaration and initialization of an assignment.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.joinLines.joinElseIf": { + "markdownDescription": "Join lines inserts else between consecutive ifs.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.joinLines.removeTrailingComma": { + "markdownDescription": "Join lines removes trailing commas.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.joinLines.unwrapTrivialBlock": { + "markdownDescription": "Join lines unwraps trivial blocks.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.lens.debug.enable": { + "markdownDescription": "Whether to show `Debug` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.lens.enable": { + "markdownDescription": "Whether to show CodeLens in Rust files.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.lens.forceCustomCommands": { + "markdownDescription": "Internal config: use custom client-side commands even when the\nclient doesn't set the corresponding capability.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.lens.implementations.enable": { + "markdownDescription": "Whether to show `Implementations` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.lens.location": { + "markdownDescription": "Where to render annotations.", + "default": "above_name", + "type": "string", + "enum": [ + "above_name", + "above_whole_item" + ], + "enumDescriptions": [ + "Render annotations above the name of the item.", + "Render annotations above the whole item, including documentation comments and attributes." + ] + }, + "rust-analyzer.lens.references.adt.enable": { + "markdownDescription": "Whether to show `References` lens for Struct, Enum, and Union.\nOnly applies when `#rust-analyzer.lens.enable#` is set.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.lens.references.enumVariant.enable": { + "markdownDescription": "Whether to show `References` lens for Enum Variants.\nOnly applies when `#rust-analyzer.lens.enable#` is set.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.lens.references.method.enable": { + "markdownDescription": "Whether to show `Method References` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.lens.references.trait.enable": { + "markdownDescription": "Whether to show `References` lens for Trait.\nOnly applies when `#rust-analyzer.lens.enable#` is set.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.lens.run.enable": { + "markdownDescription": "Whether to show `Run` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.linkedProjects": { + "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set\nof projects.\n\nElements must be paths pointing to `Cargo.toml`,\n`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON\nobjects in `rust-project.json` format.", + "default": [], + "type": "array", + "items": { + "type": [ + "string", + "object" + ] + } + }, + "rust-analyzer.lru.capacity": { + "markdownDescription": "Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.", + "default": null, + "type": [ + "null", + "integer" + ], + "minimum": 0 + }, + "rust-analyzer.lru.query.capacities": { + "markdownDescription": "Sets the LRU capacity of the specified queries.", + "default": {}, + "type": "object" + }, + "rust-analyzer.notifications.cargoTomlNotFound": { + "markdownDescription": "Whether to show `can't find Cargo.toml` error message.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.notifications.unindexedProject": { + "markdownDescription": "Whether to send an UnindexedProject notification to the client.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.numThreads": { + "markdownDescription": "How many worker threads in the main loop. The default `null` means to pick automatically.", + "default": null, + "type": [ + "null", + "integer" + ], + "minimum": 0 + }, + "rust-analyzer.procMacro.attributes.enable": { + "markdownDescription": "Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.procMacro.enable": { + "markdownDescription": "Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.procMacro.ignored": { + "markdownDescription": "These proc-macros will be ignored when trying to expand them.\n\nThis config takes a map of crate names with the exported proc-macro names to ignore as values.", + "default": {}, + "type": "object" + }, + "rust-analyzer.procMacro.server": { + "markdownDescription": "Internal config, path to proc-macro server executable.", + "default": null, + "type": [ + "null", + "string" + ] + }, + "rust-analyzer.references.excludeImports": { + "markdownDescription": "Exclude imports from find-all-references.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.references.excludeTests": { + "markdownDescription": "Exclude tests from find-all-references.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.runnables.command": { + "markdownDescription": "Command to be executed instead of 'cargo' for runnables.", + "default": null, + "type": [ + "null", + "string" + ] + }, + "rust-analyzer.runnables.extraArgs": { + "markdownDescription": "Additional arguments to be passed to cargo for runnables such as\ntests or binaries. For example, it may be `--release`.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "rust-analyzer.runnables.extraTestBinaryArgs": { + "markdownDescription": "Additional arguments to be passed through Cargo to launched tests, benchmarks, or\ndoc-tests.\n\nUnless the launched target uses a\n[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field),\nthey will end up being interpreted as options to\n[`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments).", + "default": [ + "--show-output" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "rust-analyzer.rustc.source": { + "markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private\nprojects, or \"discover\" to try to automatically find it if the `rustc-dev` component\nis installed.\n\nAny project which uses rust-analyzer with the rustcPrivate\ncrates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.\n\nThis option does not take effect until rust-analyzer is restarted.", + "default": null, + "type": [ + "null", + "string" + ] + }, + "rust-analyzer.rustfmt.extraArgs": { + "markdownDescription": "Additional arguments to `rustfmt`.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "rust-analyzer.rustfmt.overrideCommand": { + "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting. This should be the equivalent of `rustfmt` here, and\nnot that of `cargo fmt`. The file contents will be passed on the\nstandard input and the formatted result will be read from the\nstandard output.", + "default": null, + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + } + }, + "rust-analyzer.rustfmt.rangeFormatting.enable": { + "markdownDescription": "Enables the use of rustfmt's unstable range formatting command for the\n`textDocument/rangeFormatting` request. The rustfmt option is unstable and only\navailable on a nightly build.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.semanticHighlighting.doc.comment.inject.enable": { + "markdownDescription": "Inject additional highlighting into doc comments.\n\nWhen enabled, rust-analyzer will highlight rust source in doc comments as well as intra\ndoc links.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.semanticHighlighting.nonStandardTokens": { + "markdownDescription": "Whether the server is allowed to emit non-standard tokens and modifiers.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.semanticHighlighting.operator.enable": { + "markdownDescription": "Use semantic tokens for operators.\n\nWhen disabled, rust-analyzer will emit semantic tokens only for operator tokens when\nthey are tagged with modifiers.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.semanticHighlighting.operator.specialization.enable": { + "markdownDescription": "Use specialized semantic tokens for operators.\n\nWhen enabled, rust-analyzer will emit special token types for operator tokens instead\nof the generic `operator` token type.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.semanticHighlighting.punctuation.enable": { + "markdownDescription": "Use semantic tokens for punctuation.\n\nWhen disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when\nthey are tagged with modifiers or have a special role.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang": { + "markdownDescription": "When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro\ncalls.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.semanticHighlighting.punctuation.specialization.enable": { + "markdownDescription": "Use specialized semantic tokens for punctuation.\n\nWhen enabled, rust-analyzer will emit special token types for punctuation tokens instead\nof the generic `punctuation` token type.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.semanticHighlighting.strings.enable": { + "markdownDescription": "Use semantic tokens for strings.\n\nIn some editors (e.g. vscode) semantic tokens override other highlighting grammars.\nBy disabling semantic tokens for strings, other grammars can be used to highlight\ntheir contents.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.signatureInfo.detail": { + "markdownDescription": "Show full signature of the callable. Only shows parameters if disabled.", + "default": "full", + "type": "string", + "enum": [ + "full", + "parameters" + ], + "enumDescriptions": [ + "Show the entire signature.", + "Show only the parameters." + ] + }, + "rust-analyzer.signatureInfo.documentation.enable": { + "markdownDescription": "Show documentation.", + "default": true, + "type": "boolean" + }, + "rust-analyzer.typing.autoClosingAngleBrackets.enable": { + "markdownDescription": "Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list.", + "default": false, + "type": "boolean" + }, + "rust-analyzer.workspace.symbol.search.kind": { + "markdownDescription": "Workspace symbol search kind.", + "default": "only_types", + "type": "string", + "enum": [ + "only_types", + "all_symbols" + ], + "enumDescriptions": [ + "Search for types only.", + "Search for all symbols kinds." + ] + }, + "rust-analyzer.workspace.symbol.search.limit": { + "markdownDescription": "Limits the number of items returned from a workspace symbol search (Defaults to 128).\nSome clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search.\nOther clients requires all results upfront and might require a higher limit.", + "default": 128, + "type": "integer", + "minimum": 0 + }, + "rust-analyzer.workspace.symbol.search.scope": { + "markdownDescription": "Workspace symbol search scope.", + "default": "workspace", + "type": "string", + "enum": [ + "workspace", + "workspace_and_dependencies" + ], + "enumDescriptions": [ + "Search in current workspace only.", + "Search in current workspace and dependencies." + ] + }, + "$generated-end": {} + } + }, + "configurationDefaults": { + "explorer.fileNesting.patterns": { + "Cargo.toml": "Cargo.lock" + } + }, + "problemPatterns": [ + { + "name": "rustc", + "patterns": [ + { + "regexp": "^(warning|warn|error)(?:\\[(.*?)\\])?: (.*)$", + "severity": 1, + "code": 2, + "message": 3 + }, + { + "regexp": "^[\\s->=]*(.*?):([1-9]\\d*):([1-9]\\d*)\\s*$", + "file": 1, + "line": 2, + "column": 3 + } + ] + }, + { + "name": "rustc-json", + "patterns": [ + { + "regexp": "^.*\"message\":{\"message\":\"([^\"]*).*?\"file_name\":\"([^\"]+).*?\"line_start\":(\\d+).*?\"line_end\":(\\d+).*?\"column_start\":(\\d+).*?\"column_end\":(\\d+).*}$", + "message": 1, + "file": 2, + "line": 3, + "endLine": 4, + "column": 5, + "endColumn": 6 + } + ] + }, + { + "name": "rust-panic", + "patterns": [ + { + "regexp": "^thread '.*' panicked at (.*):(\\d*):(\\d*):$", + "file": 1, + "line": 2, + "column": 3 + }, + { + "regexp": "(.*)", + "message": 1 + } + ] + } + ], + "languages": [ + { + "id": "ra_syntax_tree", + "extensions": [ + ".rast" + ] + }, + { + "id": "rust", + "extensions": [ + ".rs" + ], + "aliases": [ + "Rust", + "rs" + ], + "configuration": "language-configuration.json" + } + ], + "grammars": [ + { + "language": "ra_syntax_tree", + "scopeName": "source.ra_syntax_tree", + "path": "ra_syntax_tree.tmGrammar.json" + } + ], + "problemMatchers": [ + { + "name": "rustc", + "owner": "rustc", + "source": "rustc", + "fileLocation": [ + "autoDetect", + "${workspaceRoot}" + ], + "pattern": "$rustc" + }, + { + "name": "rustc-json", + "owner": "rustc", + "source": "rustc", + "fileLocation": [ + "autoDetect", + "${workspaceRoot}" + ], + "pattern": "$rustc-json" + }, + { + "name": "rust-panic", + "owner": "rust-panic", + "source": "panic", + "fileLocation": [ + "autoDetect", + "${workspaceRoot}" + ], + "pattern": "$rust-panic" + }, + { + "name": "rustc-watch", + "owner": "rustc", + "source": "rustc", + "fileLocation": [ + "autoDetect", + "${workspaceRoot}" + ], + "background": { + "beginsPattern": "^\\[Running\\b", + "endsPattern": "^\\[Finished running\\b" + }, + "pattern": "$rustc" + } + ], + "colors": [ + { + "id": "rust_analyzer.syntaxTreeBorder", + "description": "Color of the border displayed in the Rust source code for the selected syntax node (see \"Show Syntax Tree\" command)", + "defaults": { + "dark": "#ffffff", + "light": "#b700ff", + "highContrast": "#b700ff" + } + } + ], + "semanticTokenTypes": [ + { + "id": "angle", + "description": "Style for < or >", + "superType": "punctuation" + }, + { + "id": "arithmetic", + "description": "Style for arithmetic operators", + "superType": "operator" + }, + { + "id": "attribute", + "description": "Style for attributes" + }, + { + "id": "attributeBracket", + "description": "Style for attribute invocation brackets, that is the `#[` and `]` tokens", + "superType": "punctuation" + }, + { + "id": "bitwise", + "description": "Style for bitwise operators", + "superType": "operator" + }, + { + "id": "boolean", + "description": "Style for boolean literals", + "superType": "keyword" + }, + { + "id": "brace", + "description": "Style for { or }", + "superType": "punctuation" + }, + { + "id": "bracket", + "description": "Style for [ or ]", + "superType": "punctuation" + }, + { + "id": "builtinAttribute", + "description": "Style for builtin attributes", + "superType": "attribute" + }, + { + "id": "builtinType", + "description": "Style for builtin types", + "superType": "type" + }, + { + "id": "character", + "description": "Style for character literals", + "superType": "string" + }, + { + "id": "colon", + "description": "Style for :", + "superType": "punctuation" + }, + { + "id": "comma", + "description": "Style for ,", + "superType": "punctuation" + }, + { + "id": "comparison", + "description": "Style for comparison operators", + "superType": "operator" + }, + { + "id": "constParameter", + "description": "Style for const generics" + }, + { + "id": "const", + "description": "Style for consts", + "superType": "variable" + }, + { + "id": "derive", + "description": "Style for derives", + "superType": "attribute" + }, + { + "id": "deriveHelper", + "description": "Style for derive helpers", + "superType": "attribute" + }, + { + "id": "dot", + "description": "Style for .", + "superType": "punctuation" + }, + { + "id": "escapeSequence", + "description": "Style for char or byte escapes in strings" + }, + { + "id": "formatSpecifier", + "description": "Style for {} placeholders in format strings" + }, + { + "id": "invalidEscapeSequence", + "description": "Style for invalid char or byte escapes in strings" + }, + { + "id": "label", + "description": "Style for labels" + }, + { + "id": "lifetime", + "description": "Style for lifetimes" + }, + { + "id": "logical", + "description": "Style for logic operators", + "superType": "operator" + }, + { + "id": "macroBang", + "description": "Style for the ! token of macro calls", + "superType": "punctuation" + }, + { + "id": "parenthesis", + "description": "Style for ( or )", + "superType": "punctuation" + }, + { + "id": "procMacro", + "description": "Style for proc macro code", + "superType": "macro" + }, + { + "id": "punctuation", + "description": "Style for generic punctuation" + }, + { + "id": "operator", + "description": "Style for operators", + "superType": "punctuation" + }, + { + "id": "selfKeyword", + "description": "Style for the self keyword", + "superType": "keyword" + }, + { + "id": "selfTypeKeyword", + "description": "Style for the self type keyword", + "superType": "keyword" + }, + { + "id": "semicolon", + "description": "Style for ;", + "superType": "punctuation" + }, + { + "id": "static", + "description": "Style for statics", + "superType": "variable" + }, + { + "id": "toolModule", + "description": "Style for tool module attributes", + "superType": "decorator" + }, + { + "id": "typeAlias", + "description": "Style for type aliases", + "superType": "type" + }, + { + "id": "union", + "description": "Style for C-style untagged unions", + "superType": "type" + }, + { + "id": "unresolvedReference", + "description": "Style for names which can not be resolved due to compilation errors" + } + ], + "semanticTokenModifiers": [ + { + "id": "async", + "description": "Style for async functions and the `async` and `await` keywords" + }, + { + "id": "attribute", + "description": "Style for elements within attributes" + }, + { + "id": "callable", + "description": "Style for locals whose types implements one of the `Fn*` traits" + }, + { + "id": "constant", + "description": "Style for compile-time constants" + }, + { + "id": "consuming", + "description": "Style for locals that are being consumed when use in a function call" + }, + { + "id": "controlFlow", + "description": "Style for control-flow related tokens, this includes the `?` operator" + }, + { + "id": "crateRoot", + "description": "Style for names resolving to a crate root" + }, + { + "id": "injected", + "description": "Style for doc-string injected highlighting like rust source blocks in documentation" + }, + { + "id": "intraDocLink", + "description": "Style for intra doc links in doc-strings" + }, + { + "id": "library", + "description": "Style for items that are defined outside of the current crate" + }, + { + "id": "macro", + "description": "Style for tokens inside of macro calls" + }, + { + "id": "mutable", + "description": "Style for mutable locals and statics as well as functions taking `&mut self`" + }, + { + "id": "procMacro", + "description": "Style for tokens inside of proc-macro calls" + }, + { + "id": "public", + "description": "Style for items that are from the current crate and are `pub`" + }, + { + "id": "reference", + "description": "Style for locals behind a reference and functions taking `self` by reference" + }, + { + "id": "trait", + "description": "Style for associated trait items" + }, + { + "id": "unsafe", + "description": "Style for unsafe operations, like unsafe function calls, as well as the `unsafe` token" + } + ], + "semanticTokenScopes": [ + { + "language": "rust", + "scopes": { + "attribute": [ + "meta.attribute.rust" + ], + "boolean": [ + "constant.language.boolean.rust" + ], + "builtinType": [ + "support.type.primitive.rust" + ], + "constParameter": [ + "constant.other.caps.rust" + ], + "enum": [ + "entity.name.type.enum.rust" + ], + "formatSpecifier": [ + "punctuation.section.embedded.rust" + ], + "function": [ + "entity.name.function.rust" + ], + "interface": [ + "entity.name.type.trait.rust" + ], + "keyword": [ + "keyword.other.rust" + ], + "keyword.controlFlow": [ + "keyword.control.rust" + ], + "lifetime": [ + "storage.modifier.lifetime.rust" + ], + "macroBang": [ + "entity.name.function.macro.rust" + ], + "method": [ + "entity.name.function.rust" + ], + "struct": [ + "entity.name.type.struct.rust" + ], + "typeAlias": [ + "entity.name.type.declaration.rust" + ], + "union": [ + "entity.name.type.union.rust" + ], + "variable": [ + "variable.other.rust" + ], + "variable.constant": [ + "variable.other.constant.rust" + ], + "*.mutable": [ + "markup.underline" + ] + } + } + ], + "menus": { + "commandPalette": [ + { + "command": "rust-analyzer.syntaxTree", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.viewHir", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.viewFileText", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.expandMacro", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.matchingBrace", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.parentModule", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.joinLines", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.run", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.debug", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.newDebugConfig", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.analyzerStatus", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.memoryUsage", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.reloadWorkspace", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.restartServer", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.onEnter", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.ssr", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.serverVersion", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.openDocs", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.openCargoToml", + "when": "inRustProject" + }, + { + "command": "rust-analyzer.viewMemoryLayout", + "when": "inRustProject" + } + ], + "editor/context": [ + { + "command": "rust-analyzer.peekTests", + "when": "inRustProject && editorTextFocus && editorLangId == rust", + "group": "navigation@1000" + }, + { + "command": "rust-analyzer.openDocs", + "when": "inRustProject && editorTextFocus && editorLangId == rust", + "group": "navigation@1001" + } + ] + }, + "views": { + "explorer": [ + { + "id": "rustDependencies", + "name": "Rust Dependencies", + "when": "inRustProject && config.rust-analyzer.showDependenciesExplorer" + } + ] + }, + "jsonValidation": [ + { + "fileMatch": "rust-project.json", + "url": "https://json.schemastore.org/rust-project.json" + } + ] + } +} diff --git a/src/tools/rust-analyzer/editors/code/ra_syntax_tree.tmGrammar.json b/src/tools/rust-analyzer/editors/code/ra_syntax_tree.tmGrammar.json new file mode 100644 index 00000000000..279e7bafa0b --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/ra_syntax_tree.tmGrammar.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + + "scopeName": "source.ra_syntax_tree", + "patterns": [ + { "include": "#node_type" }, + { "include": "#node_range_index" }, + { "include": "#token_text" } + ], + "repository": { + "node_type": { + "match": "^\\s*([A-Z_][A-Z_0-9]*?)@", + "captures": { + "1": { + "name": "entity.name.class" + } + } + }, + "node_range_index": { + "match": "\\d+", + "name": "constant.numeric" + }, + "token_text": { + "match": "\".+\"", + "name": "string" + } + }, + "fileTypes": ["rast"] +} diff --git a/src/tools/rust-analyzer/editors/code/src/ast_inspector.ts b/src/tools/rust-analyzer/editors/code/src/ast_inspector.ts new file mode 100644 index 00000000000..688c53a9b1f --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/ast_inspector.ts @@ -0,0 +1,217 @@ +import * as vscode from "vscode"; + +import type { Ctx, Disposable } from "./ctx"; +import { type RustEditor, isRustEditor } from "./util"; +import { unwrapUndefinable } from "./undefinable"; + +// FIXME: consider implementing this via the Tree View API? +// https://code.visualstudio.com/api/extension-guides/tree-view +export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, Disposable { + private readonly astDecorationType = vscode.window.createTextEditorDecorationType({ + borderColor: new vscode.ThemeColor("rust_analyzer.syntaxTreeBorder"), + borderStyle: "solid", + borderWidth: "2px", + }); + private rustEditor: undefined | RustEditor; + + // Lazy rust token range -> syntax tree file range. + private readonly rust2Ast = new Lazy(() => { + const astEditor = this.findAstTextEditor(); + if (!this.rustEditor || !astEditor) return undefined; + + const buf: [vscode.Range, vscode.Range][] = []; + for (let i = 0; i < astEditor.document.lineCount; ++i) { + const astLine = astEditor.document.lineAt(i); + + // Heuristically look for nodes with quoted text (which are token nodes) + const isTokenNode = astLine.text.lastIndexOf('"') >= 0; + if (!isTokenNode) continue; + + const rustRange = this.parseRustTextRange(this.rustEditor.document, astLine.text); + if (!rustRange) continue; + + buf.push([rustRange, this.findAstNodeRange(astLine)]); + } + return buf; + }); + + constructor(ctx: Ctx) { + ctx.pushExtCleanup( + vscode.languages.registerHoverProvider({ scheme: "rust-analyzer" }, this), + ); + ctx.pushExtCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this)); + vscode.workspace.onDidCloseTextDocument( + this.onDidCloseTextDocument, + this, + ctx.subscriptions, + ); + vscode.workspace.onDidChangeTextDocument( + this.onDidChangeTextDocument, + this, + ctx.subscriptions, + ); + vscode.window.onDidChangeVisibleTextEditors( + this.onDidChangeVisibleTextEditors, + this, + ctx.subscriptions, + ); + } + dispose() { + this.setRustEditor(undefined); + } + + private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { + if ( + this.rustEditor && + event.document.uri.toString() === this.rustEditor.document.uri.toString() + ) { + this.rust2Ast.reset(); + } + } + + private onDidCloseTextDocument(doc: vscode.TextDocument) { + if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) { + this.setRustEditor(undefined); + } + } + + private onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]) { + if (!this.findAstTextEditor()) { + this.setRustEditor(undefined); + return; + } + this.setRustEditor(editors.find(isRustEditor)); + } + + private findAstTextEditor(): undefined | vscode.TextEditor { + return vscode.window.visibleTextEditors.find( + (it) => it.document.uri.scheme === "rust-analyzer", + ); + } + + private setRustEditor(newRustEditor: undefined | RustEditor) { + if (this.rustEditor && this.rustEditor !== newRustEditor) { + this.rustEditor.setDecorations(this.astDecorationType, []); + this.rust2Ast.reset(); + } + this.rustEditor = newRustEditor; + } + + // additional positional params are omitted + provideDefinition( + doc: vscode.TextDocument, + pos: vscode.Position, + ): vscode.ProviderResult<vscode.DefinitionLink[]> { + if (!this.rustEditor || doc.uri.toString() !== this.rustEditor.document.uri.toString()) { + return; + } + + const astEditor = this.findAstTextEditor(); + if (!astEditor) return; + + const rust2AstRanges = this.rust2Ast + .get() + ?.find(([rustRange, _]) => rustRange.contains(pos)); + if (!rust2AstRanges) return; + + const [rustFileRange, astFileRange] = rust2AstRanges; + + astEditor.revealRange(astFileRange); + astEditor.selection = new vscode.Selection(astFileRange.start, astFileRange.end); + + return [ + { + targetRange: astFileRange, + targetUri: astEditor.document.uri, + originSelectionRange: rustFileRange, + targetSelectionRange: astFileRange, + }, + ]; + } + + // additional positional params are omitted + provideHover( + doc: vscode.TextDocument, + hoverPosition: vscode.Position, + ): vscode.ProviderResult<vscode.Hover> { + if (!this.rustEditor) return; + + const astFileLine = doc.lineAt(hoverPosition.line); + + const rustFileRange = this.parseRustTextRange(this.rustEditor.document, astFileLine.text); + if (!rustFileRange) return; + + this.rustEditor.setDecorations(this.astDecorationType, [rustFileRange]); + this.rustEditor.revealRange(rustFileRange); + + const rustSourceCode = this.rustEditor.document.getText(rustFileRange); + const astFileRange = this.findAstNodeRange(astFileLine); + + return new vscode.Hover(["```rust\n" + rustSourceCode + "\n```"], astFileRange); + } + + private findAstNodeRange(astLine: vscode.TextLine): vscode.Range { + const lineOffset = astLine.range.start; + const begin = lineOffset.translate(undefined, astLine.firstNonWhitespaceCharacterIndex); + const end = lineOffset.translate(undefined, astLine.text.trimEnd().length); + return new vscode.Range(begin, end); + } + + private parseRustTextRange( + doc: vscode.TextDocument, + astLine: string, + ): undefined | vscode.Range { + const parsedRange = /(\d+)\.\.(\d+)/.exec(astLine); + if (!parsedRange) return; + + const [begin, end] = parsedRange.slice(1).map((off) => this.positionAt(doc, +off)); + const actualBegin = unwrapUndefinable(begin); + const actualEnd = unwrapUndefinable(end); + return new vscode.Range(actualBegin, actualEnd); + } + + // Memoize the last value, otherwise the CPU is at 100% single core + // with quadratic lookups when we build rust2Ast cache + cache?: { doc: vscode.TextDocument; offset: number; line: number }; + + positionAt(doc: vscode.TextDocument, targetOffset: number): vscode.Position { + if (doc.eol === vscode.EndOfLine.LF) { + return doc.positionAt(targetOffset); + } + + // Dirty workaround for crlf line endings + // We are still in this prehistoric era of carriage returns here... + + let line = 0; + let offset = 0; + + const cache = this.cache; + if (cache?.doc === doc && cache.offset <= targetOffset) { + ({ line, offset } = cache); + } + + while (true) { + const lineLenWithLf = doc.lineAt(line).text.length + 1; + if (offset + lineLenWithLf > targetOffset) { + this.cache = { doc, offset, line }; + return doc.positionAt(targetOffset + line); + } + offset += lineLenWithLf; + line += 1; + } + } +} + +class Lazy<T> { + val: undefined | T; + + constructor(private readonly compute: () => undefined | T) {} + + get() { + return this.val ?? (this.val = this.compute()); + } + + reset() { + this.val = undefined; + } +} diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts new file mode 100644 index 00000000000..6cf399599d9 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -0,0 +1,144 @@ +import * as vscode from "vscode"; +import * as os from "os"; +import type { Config } from "./config"; +import { log, isValidExecutable } from "./util"; +import type { PersistentState } from "./persistent_state"; +import { exec } from "child_process"; + +export async function bootstrap( + context: vscode.ExtensionContext, + config: Config, + state: PersistentState, +): Promise<string> { + const path = await getServer(context, config, state); + if (!path) { + throw new Error( + "Rust Analyzer Language Server is not available. " + + "Please, ensure its [proper installation](https://rust-analyzer.github.io/manual.html#installation).", + ); + } + + log.info("Using server binary at", path); + + if (!isValidExecutable(path, config.serverExtraEnv)) { + if (config.serverPath) { + throw new Error(`Failed to execute ${path} --version. \`config.server.path\` or \`config.serverPath\` has been set explicitly.\ + Consider removing this config or making a valid server binary available at that path.`); + } else { + throw new Error(`Failed to execute ${path} --version`); + } + } + + return path; +} +async function getServer( + context: vscode.ExtensionContext, + config: Config, + state: PersistentState, +): Promise<string | undefined> { + const explicitPath = process.env["__RA_LSP_SERVER_DEBUG"] ?? config.serverPath; + if (explicitPath) { + if (explicitPath.startsWith("~/")) { + return os.homedir() + explicitPath.slice("~".length); + } + return explicitPath; + } + if (config.package.releaseTag === null) return "rust-analyzer"; + + const ext = process.platform === "win32" ? ".exe" : ""; + const bundled = vscode.Uri.joinPath(context.extensionUri, "server", `rust-analyzer${ext}`); + const bundledExists = await vscode.workspace.fs.stat(bundled).then( + () => true, + () => false, + ); + if (bundledExists) { + let server = bundled; + if (await isNixOs()) { + await vscode.workspace.fs.createDirectory(config.globalStorageUri).then(); + const dest = vscode.Uri.joinPath(config.globalStorageUri, `rust-analyzer${ext}`); + let exists = await vscode.workspace.fs.stat(dest).then( + () => true, + () => false, + ); + if (exists && config.package.version !== state.serverVersion) { + await vscode.workspace.fs.delete(dest); + exists = false; + } + if (!exists) { + await vscode.workspace.fs.copy(bundled, dest); + await patchelf(dest); + } + server = dest; + } + await state.updateServerVersion(config.package.version); + return server.fsPath; + } + + await state.updateServerVersion(undefined); + await vscode.window.showErrorMessage( + "Unfortunately we don't ship binaries for your platform yet. " + + "You need to manually clone the rust-analyzer repository and " + + "run `cargo xtask install --server` to build the language server from sources. " + + "If you feel that your platform should be supported, please create an issue " + + "about that [here](https://github.com/rust-lang/rust-analyzer/issues) and we " + + "will consider it.", + ); + return undefined; +} + +async function isNixOs(): Promise<boolean> { + try { + const contents = ( + await vscode.workspace.fs.readFile(vscode.Uri.file("/etc/os-release")) + ).toString(); + const idString = contents.split("\n").find((a) => a.startsWith("ID=")) || "ID=linux"; + return idString.indexOf("nixos") !== -1; + } catch { + return false; + } +} + +async function patchelf(dest: vscode.Uri): Promise<void> { + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: "Patching rust-analyzer for NixOS", + }, + async (progress, _) => { + const expression = ` + {srcStr, pkgs ? import <nixpkgs> {}}: + pkgs.stdenv.mkDerivation { + name = "rust-analyzer"; + src = /. + srcStr; + phases = [ "installPhase" "fixupPhase" ]; + installPhase = "cp $src $out"; + fixupPhase = '' + chmod 755 $out + patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" $out + ''; + } + `; + const origFile = vscode.Uri.file(dest.fsPath + "-orig"); + await vscode.workspace.fs.rename(dest, origFile, { overwrite: true }); + try { + progress.report({ message: "Patching executable", increment: 20 }); + await new Promise((resolve, reject) => { + const handle = exec( + `nix-build -E - --argstr srcStr '${origFile.fsPath}' -o '${dest.fsPath}'`, + (err, stdout, stderr) => { + if (err != null) { + reject(Error(stderr)); + } else { + resolve(stdout); + } + }, + ); + handle.stdin?.write(expression); + handle.stdin?.end(); + }); + } finally { + await vscode.workspace.fs.delete(origFile); + } + }, + ); +} diff --git a/src/tools/rust-analyzer/editors/code/src/client.ts b/src/tools/rust-analyzer/editors/code/src/client.ts new file mode 100644 index 00000000000..372dc8bedfd --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/client.ts @@ -0,0 +1,452 @@ +import * as anser from "anser"; +import * as lc from "vscode-languageclient/node"; +import * as vscode from "vscode"; +import * as ra from "../src/lsp_ext"; +import * as Is from "vscode-languageclient/lib/common/utils/is"; +import { assert } from "./util"; +import * as diagnostics from "./diagnostics"; +import { WorkspaceEdit } from "vscode"; +import { type Config, prepareVSCodeConfig } from "./config"; +import { randomUUID } from "crypto"; +import { sep as pathSeparator } from "path"; +import { unwrapUndefinable } from "./undefinable"; +import { RaLanguageClient } from "./lang_client"; + +export interface Env { + [name: string]: string; +} + +// Command URIs have a form of command:command-name?arguments, where +// arguments is a percent-encoded array of data we want to pass along to +// the command function. For "Show References" this is a list of all file +// URIs with locations of every reference, and it can get quite long. +// +// To work around it we use an intermediary linkToCommand command. When +// we render a command link, a reference to a command with all its arguments +// is stored in a map, and instead a linkToCommand link is rendered +// with the key to that map. +export const LINKED_COMMANDS = new Map<string, ra.CommandLink>(); + +// For now the map is cleaned up periodically (I've set it to every +// 10 minutes). In general case we'll probably need to introduce TTLs or +// flags to denote ephemeral links (like these in hover popups) and +// persistent links and clean those separately. But for now simply keeping +// the last few links in the map should be good enough. Likewise, we could +// add code to remove a target command from the map after the link is +// clicked, but assuming most links in hover sheets won't be clicked anyway +// this code won't change the overall memory use much. +setInterval( + function cleanupOlderCommandLinks() { + // keys are returned in insertion order, we'll keep a few + // of recent keys available, and clean the rest + const keys = [...LINKED_COMMANDS.keys()]; + const keysToRemove = keys.slice(0, keys.length - 10); + for (const key of keysToRemove) { + LINKED_COMMANDS.delete(key); + } + }, + 10 * 60 * 1000, +); + +function renderCommand(cmd: ra.CommandLink): string { + const commandId = randomUUID(); + LINKED_COMMANDS.set(commandId, cmd); + return `[${cmd.title}](command:rust-analyzer.linkToCommand?${encodeURIComponent( + JSON.stringify([commandId]), + )} '${cmd.tooltip}')`; +} + +function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownString { + const text = actions + .map( + (group) => + (group.title ? group.title + " " : "") + + group.commands.map(renderCommand).join(" | "), + ) + .join("___"); + + const result = new vscode.MarkdownString(text); + result.isTrusted = true; + return result; +} + +export async function createClient( + traceOutputChannel: vscode.OutputChannel, + outputChannel: vscode.OutputChannel, + initializationOptions: vscode.WorkspaceConfiguration, + serverOptions: lc.ServerOptions, + config: Config, + unlinkedFiles: vscode.Uri[], +): Promise<lc.LanguageClient> { + const clientOptions: lc.LanguageClientOptions = { + documentSelector: [{ scheme: "file", language: "rust" }], + initializationOptions, + diagnosticCollectionName: "rustc", + traceOutputChannel, + outputChannel, + middleware: { + workspace: { + // HACK: This is a workaround, when the client has been disposed, VSCode + // continues to emit events to the client and the default one for this event + // attempt to restart the client for no reason + async didChangeWatchedFile(event, next) { + if (client.isRunning()) { + await next(event); + } + }, + async configuration( + params: lc.ConfigurationParams, + token: vscode.CancellationToken, + next: lc.ConfigurationRequest.HandlerSignature, + ) { + const resp = await next(params, token); + if (resp && Array.isArray(resp)) { + return resp.map((val) => { + return prepareVSCodeConfig(val, (key, cfg) => { + // we only want to set discovered workspaces on the right key + // and if a workspace has been discovered. + if ( + key === "linkedProjects" && + config.discoveredWorkspaces.length > 0 + ) { + cfg[key] = config.discoveredWorkspaces; + } + }); + }); + } else { + return resp; + } + }, + }, + async handleDiagnostics( + uri: vscode.Uri, + diagnosticList: vscode.Diagnostic[], + next: lc.HandleDiagnosticsSignature, + ) { + const preview = config.previewRustcOutput; + const errorCode = config.useRustcErrorCode; + diagnosticList.forEach((diag, idx) => { + const value = + typeof diag.code === "string" || typeof diag.code === "number" + ? diag.code + : diag.code?.value; + if ( + // FIXME: We currently emit this diagnostic way too early, before we have + // loaded the project fully + // value === "unlinked-file" && + value === "temporary-disabled" && + !unlinkedFiles.includes(uri) && + diag.message !== "file not included in module tree" + ) { + const config = vscode.workspace.getConfiguration("rust-analyzer"); + if (config.get("showUnlinkedFileNotification")) { + unlinkedFiles.push(uri); + const folder = vscode.workspace.getWorkspaceFolder(uri)?.uri.fsPath; + if (folder) { + const parentBackslash = uri.fsPath.lastIndexOf( + pathSeparator + "src", + ); + const parent = uri.fsPath.substring(0, parentBackslash); + + if (parent.startsWith(folder)) { + const path = vscode.Uri.file( + parent + pathSeparator + "Cargo.toml", + ); + void vscode.workspace.fs.stat(path).then(async () => { + const choice = await vscode.window.showInformationMessage( + `This rust file does not belong to a loaded cargo project. It looks like it might belong to the workspace at ${path.path}, do you want to add it to the linked Projects?`, + "Yes", + "No", + "Don't show this again", + ); + switch (choice) { + case undefined: + break; + case "No": + break; + case "Yes": + const pathToInsert = + "." + + parent.substring(folder.length) + + pathSeparator + + "Cargo.toml"; + await config.update( + "linkedProjects", + config + .get<any[]>("linkedProjects") + ?.concat(pathToInsert), + false, + ); + break; + case "Don't show this again": + await config.update( + "showUnlinkedFileNotification", + false, + false, + ); + break; + } + }); + } + } + } + } + + // Abuse the fact that VSCode leaks the LSP diagnostics data field through the + // Diagnostic class, if they ever break this we are out of luck and have to go + // back to the worst diagnostics experience ever:) + + // We encode the rendered output of a rustc diagnostic in the rendered field of + // the data payload of the lsp diagnostic. If that field exists, overwrite the + // diagnostic code such that clicking it opens the diagnostic in a readonly + // text editor for easy inspection + const rendered = (diag as unknown as { data?: { rendered?: string } }).data + ?.rendered; + if (rendered) { + if (preview) { + const decolorized = anser.ansiToText(rendered); + const index = + decolorized.match(/^(note|help):/m)?.index || rendered.length; + diag.message = decolorized + .substring(0, index) + .replace(/^ -->[^\n]+\n/m, ""); + } + diag.code = { + target: vscode.Uri.from({ + scheme: diagnostics.URI_SCHEME, + path: `/diagnostic message [${idx.toString()}]`, + fragment: uri.toString(), + query: idx.toString(), + }), + value: + errorCode && value ? value : "Click for full compiler diagnostic", + }; + } + }); + return next(uri, diagnosticList); + }, + async provideHover( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken, + _next: lc.ProvideHoverSignature, + ) { + const editor = vscode.window.activeTextEditor; + const positionOrRange = editor?.selection?.contains(position) + ? client.code2ProtocolConverter.asRange(editor.selection) + : client.code2ProtocolConverter.asPosition(position); + return client + .sendRequest( + ra.hover, + { + textDocument: + client.code2ProtocolConverter.asTextDocumentIdentifier(document), + position: positionOrRange, + }, + token, + ) + .then( + (result) => { + if (!result) return null; + const hover = client.protocol2CodeConverter.asHover(result); + if (!!result.actions) { + hover.contents.push(renderHoverActions(result.actions)); + } + return hover; + }, + (error) => { + client.handleFailedRequest(lc.HoverRequest.type, token, error, null); + return Promise.resolve(null); + }, + ); + }, + // Using custom handling of CodeActions to support action groups and snippet edits. + // Note that this means we have to re-implement lazy edit resolving ourselves as well. + async provideCodeActions( + document: vscode.TextDocument, + range: vscode.Range, + context: vscode.CodeActionContext, + token: vscode.CancellationToken, + _next: lc.ProvideCodeActionsSignature, + ) { + const params: lc.CodeActionParams = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), + range: client.code2ProtocolConverter.asRange(range), + context: await client.code2ProtocolConverter.asCodeActionContext( + context, + token, + ), + }; + return client.sendRequest(lc.CodeActionRequest.type, params, token).then( + async (values) => { + if (values === null) return undefined; + const result: (vscode.CodeAction | vscode.Command)[] = []; + const groups = new Map< + string, + { index: number; items: vscode.CodeAction[] } + >(); + for (const item of values) { + // In our case we expect to get code edits only from diagnostics + if (lc.CodeAction.is(item)) { + assert( + !item.command, + "We don't expect to receive commands in CodeActions", + ); + const action = await client.protocol2CodeConverter.asCodeAction( + item, + token, + ); + result.push(action); + continue; + } + assert( + isCodeActionWithoutEditsAndCommands(item), + "We don't expect edits or commands here", + ); + const kind = client.protocol2CodeConverter.asCodeActionKind( + (item as any).kind, + ); + const action = new vscode.CodeAction(item.title, kind); + const group = (item as any).group; + action.command = { + command: "rust-analyzer.resolveCodeAction", + title: item.title, + arguments: [item], + }; + + // Set a dummy edit, so that VS Code doesn't try to resolve this. + action.edit = new WorkspaceEdit(); + + if (group) { + let entry = groups.get(group); + if (!entry) { + entry = { index: result.length, items: [] }; + groups.set(group, entry); + result.push(action); + } + entry.items.push(action); + } else { + result.push(action); + } + } + for (const [group, { index, items }] of groups) { + if (items.length === 1) { + const item = unwrapUndefinable(items[0]); + result[index] = item; + } else { + const action = new vscode.CodeAction(group); + const item = unwrapUndefinable(items[0]); + action.kind = item.kind; + action.command = { + command: "rust-analyzer.applyActionGroup", + title: "", + arguments: [ + items.map((item) => { + return { + label: item.title, + arguments: item.command!.arguments![0], + }; + }), + ], + }; + + // Set a dummy edit, so that VS Code doesn't try to resolve this. + action.edit = new WorkspaceEdit(); + + result[index] = action; + } + } + return result; + }, + (_error) => undefined, + ); + }, + }, + markdown: { + supportHtml: true, + }, + }; + + const client = new RaLanguageClient( + "rust-analyzer", + "Rust Analyzer Language Server", + serverOptions, + clientOptions, + ); + + // To turn on all proposed features use: client.registerProposedFeatures(); + client.registerFeature(new ExperimentalFeatures(config)); + client.registerFeature(new OverrideFeatures()); + + return client; +} + +class ExperimentalFeatures implements lc.StaticFeature { + private readonly testExplorer: boolean; + + constructor(config: Config) { + this.testExplorer = config.testExplorer || false; + } + getState(): lc.FeatureState { + return { kind: "static" }; + } + fillClientCapabilities(capabilities: lc.ClientCapabilities): void { + capabilities.experimental = { + snippetTextEdit: true, + codeActionGroup: true, + hoverActions: true, + serverStatusNotification: true, + colorDiagnosticOutput: true, + openServerLogs: true, + localDocs: true, + testExplorer: this.testExplorer, + commands: { + commands: [ + "rust-analyzer.runSingle", + "rust-analyzer.debugSingle", + "rust-analyzer.showReferences", + "rust-analyzer.gotoLocation", + "editor.action.triggerParameterHints", + ], + }, + ...capabilities.experimental, + }; + } + initialize( + _capabilities: lc.ServerCapabilities, + _documentSelector: lc.DocumentSelector | undefined, + ): void {} + dispose(): void {} +} + +class OverrideFeatures implements lc.StaticFeature { + getState(): lc.FeatureState { + return { kind: "static" }; + } + fillClientCapabilities(capabilities: lc.ClientCapabilities): void { + // Force disable `augmentsSyntaxTokens`, VSCode's textmate grammar is somewhat incomplete + // making the experience generally worse + const caps = capabilities.textDocument?.semanticTokens; + if (caps) { + caps.augmentsSyntaxTokens = false; + } + } + initialize( + _capabilities: lc.ServerCapabilities, + _documentSelector: lc.DocumentSelector | undefined, + ): void {} + dispose(): void {} +} + +function isCodeActionWithoutEditsAndCommands(value: any): boolean { + const candidate: lc.CodeAction = value; + return ( + candidate && + Is.string(candidate.title) && + (candidate.diagnostics === void 0 || + Is.typedArray(candidate.diagnostics, lc.Diagnostic.is)) && + (candidate.kind === void 0 || Is.string(candidate.kind)) && + candidate.edit === void 0 && + candidate.command === void 0 + ); +} diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts new file mode 100644 index 00000000000..849fae5cf24 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/commands.ts @@ -0,0 +1,1488 @@ +import * as vscode from "vscode"; +import * as lc from "vscode-languageclient"; +import * as ra from "./lsp_ext"; +import * as path from "path"; + +import type { Ctx, Cmd, CtxInit } from "./ctx"; +import { + applySnippetWorkspaceEdit, + applySnippetTextEdits, + type SnippetTextDocumentEdit, +} from "./snippets"; +import { spawnSync } from "child_process"; +import { type RunnableQuickPick, selectRunnable, createTask, createArgs } from "./run"; +import { AstInspector } from "./ast_inspector"; +import { + isRustDocument, + isCargoTomlDocument, + sleep, + isRustEditor, + type RustEditor, + type RustDocument, +} from "./util"; +import { startDebugSession, makeDebugConfig } from "./debug"; +import type { LanguageClient } from "vscode-languageclient/node"; +import { LINKED_COMMANDS } from "./client"; +import type { DependencyId } from "./dependencies_provider"; +import { unwrapUndefinable } from "./undefinable"; +import { log } from "./util"; + +export * from "./ast_inspector"; +export * from "./run"; + +export function analyzerStatus(ctx: CtxInit): Cmd { + const tdcp = new (class implements vscode.TextDocumentContentProvider { + readonly uri = vscode.Uri.parse("rust-analyzer-status://status"); + readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); + + async provideTextDocumentContent(_uri: vscode.Uri): Promise<string> { + if (!vscode.window.activeTextEditor) return ""; + const client = ctx.client; + + const params: ra.AnalyzerStatusParams = {}; + const doc = ctx.activeRustEditor?.document; + if (doc != null) { + params.textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(doc); + } + return await client.sendRequest(ra.analyzerStatus, params); + } + + get onDidChange(): vscode.Event<vscode.Uri> { + return this.eventEmitter.event; + } + })(); + + ctx.pushExtCleanup( + vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-status", tdcp), + ); + + return async () => { + const document = await vscode.workspace.openTextDocument(tdcp.uri); + tdcp.eventEmitter.fire(tdcp.uri); + void (await vscode.window.showTextDocument(document, { + viewColumn: vscode.ViewColumn.Two, + preserveFocus: true, + })); + }; +} + +export function memoryUsage(ctx: CtxInit): Cmd { + const tdcp = new (class implements vscode.TextDocumentContentProvider { + readonly uri = vscode.Uri.parse("rust-analyzer-memory://memory"); + readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); + + provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> { + if (!vscode.window.activeTextEditor) return ""; + + return ctx.client.sendRequest(ra.memoryUsage).then((mem: any) => { + return "Per-query memory usage:\n" + mem + "\n(note: database has been cleared)"; + }); + } + + get onDidChange(): vscode.Event<vscode.Uri> { + return this.eventEmitter.event; + } + })(); + + ctx.pushExtCleanup( + vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-memory", tdcp), + ); + + return async () => { + tdcp.eventEmitter.fire(tdcp.uri); + const document = await vscode.workspace.openTextDocument(tdcp.uri); + return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true); + }; +} + +export function shuffleCrateGraph(ctx: CtxInit): Cmd { + return async () => { + return ctx.client.sendRequest(ra.shuffleCrateGraph); + }; +} + +export function triggerParameterHints(_: CtxInit): Cmd { + return async () => { + const parameterHintsEnabled = vscode.workspace + .getConfiguration("editor") + .get<boolean>("parameterHints.enabled"); + + if (parameterHintsEnabled) { + await vscode.commands.executeCommand("editor.action.triggerParameterHints"); + } + }; +} + +export function openLogs(ctx: CtxInit): Cmd { + return async () => { + if (ctx.client.outputChannel) { + ctx.client.outputChannel.show(); + } + }; +} + +export function matchingBrace(ctx: CtxInit): Cmd { + return async () => { + const editor = ctx.activeRustEditor; + if (!editor) return; + + const client = ctx.client; + + const response = await client.sendRequest(ra.matchingBrace, { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), + positions: editor.selections.map((s) => + client.code2ProtocolConverter.asPosition(s.active), + ), + }); + editor.selections = editor.selections.map((sel, idx) => { + const position = unwrapUndefinable(response[idx]); + const active = client.protocol2CodeConverter.asPosition(position); + const anchor = sel.isEmpty ? active : sel.anchor; + return new vscode.Selection(anchor, active); + }); + editor.revealRange(editor.selection); + }; +} + +export function joinLines(ctx: CtxInit): Cmd { + return async () => { + const editor = ctx.activeRustEditor; + if (!editor) return; + + const client = ctx.client; + + const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, { + ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)), + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), + }); + const textEdits = await client.protocol2CodeConverter.asTextEdits(items); + await editor.edit((builder) => { + textEdits.forEach((edit: any) => { + builder.replace(edit.range, edit.newText); + }); + }); + }; +} + +export function moveItemUp(ctx: CtxInit): Cmd { + return moveItem(ctx, "Up"); +} + +export function moveItemDown(ctx: CtxInit): Cmd { + return moveItem(ctx, "Down"); +} + +export function moveItem(ctx: CtxInit, direction: ra.Direction): Cmd { + return async () => { + const editor = ctx.activeRustEditor; + if (!editor) return; + const client = ctx.client; + + const lcEdits = await client.sendRequest(ra.moveItem, { + range: client.code2ProtocolConverter.asRange(editor.selection), + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), + direction, + }); + + if (!lcEdits) return; + + const edits = await client.protocol2CodeConverter.asTextEdits(lcEdits); + await applySnippetTextEdits(editor, edits); + }; +} + +export function onEnter(ctx: CtxInit): Cmd { + async function handleKeypress() { + const editor = ctx.activeRustEditor; + + if (!editor) return false; + + const client = ctx.client; + const lcEdits = await client + .sendRequest(ra.onEnter, { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier( + editor.document, + ), + position: client.code2ProtocolConverter.asPosition(editor.selection.active), + }) + .catch((_error: any) => { + // client.handleFailedRequest(OnEnterRequest.type, error, null); + return null; + }); + if (!lcEdits) return false; + + const edits = await client.protocol2CodeConverter.asTextEdits(lcEdits); + await applySnippetTextEdits(editor, edits); + return true; + } + + return async () => { + if (await handleKeypress()) return; + + await vscode.commands.executeCommand("default:type", { text: "\n" }); + }; +} + +export function parentModule(ctx: CtxInit): Cmd { + return async () => { + const editor = vscode.window.activeTextEditor; + if (!editor) return; + if (!(isRustDocument(editor.document) || isCargoTomlDocument(editor.document))) return; + + const client = ctx.client; + + const locations = await client.sendRequest(ra.parentModule, { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), + position: client.code2ProtocolConverter.asPosition(editor.selection.active), + }); + if (!locations) return; + + if (locations.length === 1) { + const loc = unwrapUndefinable(locations[0]); + + const uri = client.protocol2CodeConverter.asUri(loc.targetUri); + const range = client.protocol2CodeConverter.asRange(loc.targetRange); + + const doc = await vscode.workspace.openTextDocument(uri); + const e = await vscode.window.showTextDocument(doc); + e.selection = new vscode.Selection(range.start, range.start); + e.revealRange(range, vscode.TextEditorRevealType.InCenter); + } else { + const uri = editor.document.uri.toString(); + const position = client.code2ProtocolConverter.asPosition(editor.selection.active); + await showReferencesImpl( + client, + uri, + position, + locations.map((loc) => lc.Location.create(loc.targetUri, loc.targetRange)), + ); + } + }; +} + +export function openCargoToml(ctx: CtxInit): Cmd { + return async () => { + const editor = ctx.activeRustEditor; + if (!editor) return; + + const client = ctx.client; + const response = await client.sendRequest(ra.openCargoToml, { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), + }); + if (!response) return; + + const uri = client.protocol2CodeConverter.asUri(response.uri); + const range = client.protocol2CodeConverter.asRange(response.range); + + const doc = await vscode.workspace.openTextDocument(uri); + const e = await vscode.window.showTextDocument(doc); + e.selection = new vscode.Selection(range.start, range.start); + e.revealRange(range, vscode.TextEditorRevealType.InCenter); + }; +} + +export function revealDependency(ctx: CtxInit): Cmd { + return async (editor: RustEditor) => { + if (!ctx.dependencies?.isInitialized()) { + return; + } + const documentPath = editor.document.uri.fsPath; + const dep = ctx.dependencies?.getDependency(documentPath); + if (dep) { + await ctx.treeView?.reveal(dep, { select: true, expand: true }); + } else { + await revealParentChain(editor.document, ctx); + } + }; +} + +/** + * This function calculates the parent chain of a given file until it reaches it crate root contained in ctx.dependencies. + * This is need because the TreeView is Lazy, so at first it only has the root dependencies: For example if we have the following crates: + * - core + * - alloc + * - std + * + * if I want to reveal alloc/src/str.rs, I have to: + + * 1. reveal every children of alloc + * - core + * - alloc\ + *  |-beches\ + *  |-src\ + *  |- ... + * - std + * 2. reveal every children of src: + * core + * alloc\ + *  |-beches\ + *  |-src\ + *   |- lib.rs\ + *   |- str.rs <------- FOUND IT!\ + *   |- ...\ + *  |- ...\ + * std + */ +async function revealParentChain(document: RustDocument, ctx: CtxInit) { + let documentPath = document.uri.fsPath; + const maxDepth = documentPath.split(path.sep).length - 1; + const parentChain: DependencyId[] = [{ id: documentPath.toLowerCase() }]; + do { + documentPath = path.dirname(documentPath); + parentChain.push({ id: documentPath.toLowerCase() }); + if (parentChain.length >= maxDepth) { + // this is an odd case that can happen when we change a crate version but we'd still have + // a open file referencing the old version + return; + } + } while (!ctx.dependencies?.contains(documentPath)); + parentChain.reverse(); + for (const idx in parentChain) { + const treeView = ctx.treeView; + if (!treeView) { + continue; + } + + const dependency = unwrapUndefinable(parentChain[idx]); + await treeView.reveal(dependency, { select: true, expand: true }); + } +} + +export async function execRevealDependency(e: RustEditor): Promise<void> { + await vscode.commands.executeCommand("rust-analyzer.revealDependency", e); +} + +export function ssr(ctx: CtxInit): Cmd { + return async () => { + const editor = vscode.window.activeTextEditor; + if (!editor) return; + + const client = ctx.client; + + const position = editor.selection.active; + const selections = editor.selections; + const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier( + editor.document, + ); + + const options: vscode.InputBoxOptions = { + value: "() ==>> ()", + prompt: "Enter request, for example 'Foo($a) ==>> Foo::new($a)' ", + validateInput: async (x: string) => { + try { + await client.sendRequest(ra.ssr, { + query: x, + parseOnly: true, + textDocument, + position, + selections, + }); + } catch (e) { + return String(e); + } + return null; + }, + }; + const request = await vscode.window.showInputBox(options); + if (!request) return; + + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: "Structured search replace in progress...", + cancellable: false, + }, + async (_progress, token) => { + const edit = await client.sendRequest(ra.ssr, { + query: request, + parseOnly: false, + textDocument, + position, + selections, + }); + + await vscode.workspace.applyEdit( + await client.protocol2CodeConverter.asWorkspaceEdit(edit, token), + ); + }, + ); + }; +} + +export function serverVersion(ctx: CtxInit): Cmd { + return async () => { + if (!ctx.serverPath) { + void vscode.window.showWarningMessage(`rust-analyzer server is not running`); + return; + } + const { stdout } = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" }); + const versionString = stdout.slice(`rust-analyzer `.length).trim(); + + void vscode.window.showInformationMessage(`rust-analyzer version: ${versionString}`); + }; +} + +// Opens the virtual file that will show the syntax tree +// +// The contents of the file come from the `TextDocumentContentProvider` +export function syntaxTree(ctx: CtxInit): Cmd { + const tdcp = new (class implements vscode.TextDocumentContentProvider { + readonly uri = vscode.Uri.parse("rust-analyzer-syntax-tree://syntaxtree/tree.rast"); + readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); + constructor() { + vscode.workspace.onDidChangeTextDocument( + this.onDidChangeTextDocument, + this, + ctx.subscriptions, + ); + vscode.window.onDidChangeActiveTextEditor( + this.onDidChangeActiveTextEditor, + this, + ctx.subscriptions, + ); + } + + private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { + if (isRustDocument(event.document)) { + // We need to order this after language server updates, but there's no API for that. + // Hence, good old sleep(). + void sleep(10).then(() => this.eventEmitter.fire(this.uri)); + } + } + private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) { + if (editor && isRustEditor(editor)) { + this.eventEmitter.fire(this.uri); + } + } + + async provideTextDocumentContent( + uri: vscode.Uri, + ct: vscode.CancellationToken, + ): Promise<string> { + const rustEditor = ctx.activeRustEditor; + if (!rustEditor) return ""; + const client = ctx.client; + + // When the range based query is enabled we take the range of the selection + const range = + uri.query === "range=true" && !rustEditor.selection.isEmpty + ? client.code2ProtocolConverter.asRange(rustEditor.selection) + : null; + + const params = { textDocument: { uri: rustEditor.document.uri.toString() }, range }; + return client.sendRequest(ra.syntaxTree, params, ct); + } + + get onDidChange(): vscode.Event<vscode.Uri> { + return this.eventEmitter.event; + } + })(); + + ctx.pushExtCleanup(new AstInspector(ctx)); + ctx.pushExtCleanup( + vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-syntax-tree", tdcp), + ); + ctx.pushExtCleanup( + vscode.languages.setLanguageConfiguration("ra_syntax_tree", { + brackets: [["[", ")"]], + }), + ); + + return async () => { + const editor = vscode.window.activeTextEditor; + const rangeEnabled = !!editor && !editor.selection.isEmpty; + + const uri = rangeEnabled ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`) : tdcp.uri; + + const document = await vscode.workspace.openTextDocument(uri); + + tdcp.eventEmitter.fire(uri); + + void (await vscode.window.showTextDocument(document, { + viewColumn: vscode.ViewColumn.Two, + preserveFocus: true, + })); + }; +} + +function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd { + const viewXir = xir === "hir" ? "viewHir" : "viewMir"; + const requestType = xir === "hir" ? ra.viewHir : ra.viewMir; + const uri = `rust-analyzer-${xir}://${viewXir}/${xir}.rs`; + const scheme = `rust-analyzer-${xir}`; + return viewFileUsingTextDocumentContentProvider(ctx, requestType, uri, scheme, true); +} + +function viewFileUsingTextDocumentContentProvider( + ctx: CtxInit, + requestType: lc.RequestType<lc.TextDocumentPositionParams, string, void>, + uri: string, + scheme: string, + shouldUpdate: boolean, +): Cmd { + const tdcp = new (class implements vscode.TextDocumentContentProvider { + readonly uri = vscode.Uri.parse(uri); + readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); + constructor() { + vscode.workspace.onDidChangeTextDocument( + this.onDidChangeTextDocument, + this, + ctx.subscriptions, + ); + vscode.window.onDidChangeActiveTextEditor( + this.onDidChangeActiveTextEditor, + this, + ctx.subscriptions, + ); + } + + private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { + if (isRustDocument(event.document) && shouldUpdate) { + // We need to order this after language server updates, but there's no API for that. + // Hence, good old sleep(). + void sleep(10).then(() => this.eventEmitter.fire(this.uri)); + } + } + private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) { + if (editor && isRustEditor(editor) && shouldUpdate) { + this.eventEmitter.fire(this.uri); + } + } + + async provideTextDocumentContent( + _uri: vscode.Uri, + ct: vscode.CancellationToken, + ): Promise<string> { + const rustEditor = ctx.activeRustEditor; + if (!rustEditor) return ""; + + const client = ctx.client; + const params = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier( + rustEditor.document, + ), + position: client.code2ProtocolConverter.asPosition(rustEditor.selection.active), + }; + return client.sendRequest(requestType, params, ct); + } + + get onDidChange(): vscode.Event<vscode.Uri> { + return this.eventEmitter.event; + } + })(); + + ctx.pushExtCleanup(vscode.workspace.registerTextDocumentContentProvider(scheme, tdcp)); + + return async () => { + const document = await vscode.workspace.openTextDocument(tdcp.uri); + tdcp.eventEmitter.fire(tdcp.uri); + void (await vscode.window.showTextDocument(document, { + viewColumn: vscode.ViewColumn.Two, + preserveFocus: true, + })); + }; +} + +// Opens the virtual file that will show the HIR of the function containing the cursor position +// +// The contents of the file come from the `TextDocumentContentProvider` +export function viewHir(ctx: CtxInit): Cmd { + return viewHirOrMir(ctx, "hir"); +} + +// Opens the virtual file that will show the MIR of the function containing the cursor position +// +// The contents of the file come from the `TextDocumentContentProvider` +export function viewMir(ctx: CtxInit): Cmd { + return viewHirOrMir(ctx, "mir"); +} + +// Opens the virtual file that will show the MIR of the function containing the cursor position +// +// The contents of the file come from the `TextDocumentContentProvider` +export function interpretFunction(ctx: CtxInit): Cmd { + const uri = `rust-analyzer-interpret-function://interpretFunction/result.log`; + return viewFileUsingTextDocumentContentProvider( + ctx, + ra.interpretFunction, + uri, + `rust-analyzer-interpret-function`, + false, + ); +} + +export function viewFileText(ctx: CtxInit): Cmd { + const tdcp = new (class implements vscode.TextDocumentContentProvider { + readonly uri = vscode.Uri.parse("rust-analyzer-file-text://viewFileText/file.rs"); + readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); + constructor() { + vscode.workspace.onDidChangeTextDocument( + this.onDidChangeTextDocument, + this, + ctx.subscriptions, + ); + vscode.window.onDidChangeActiveTextEditor( + this.onDidChangeActiveTextEditor, + this, + ctx.subscriptions, + ); + } + + private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { + if (isRustDocument(event.document)) { + // We need to order this after language server updates, but there's no API for that. + // Hence, good old sleep(). + void sleep(10).then(() => this.eventEmitter.fire(this.uri)); + } + } + private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) { + if (editor && isRustEditor(editor)) { + this.eventEmitter.fire(this.uri); + } + } + + async provideTextDocumentContent( + _uri: vscode.Uri, + ct: vscode.CancellationToken, + ): Promise<string> { + const rustEditor = ctx.activeRustEditor; + if (!rustEditor) return ""; + const client = ctx.client; + + const params = client.code2ProtocolConverter.asTextDocumentIdentifier( + rustEditor.document, + ); + return client.sendRequest(ra.viewFileText, params, ct); + } + + get onDidChange(): vscode.Event<vscode.Uri> { + return this.eventEmitter.event; + } + })(); + + ctx.pushExtCleanup( + vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-file-text", tdcp), + ); + + return async () => { + const document = await vscode.workspace.openTextDocument(tdcp.uri); + tdcp.eventEmitter.fire(tdcp.uri); + void (await vscode.window.showTextDocument(document, { + viewColumn: vscode.ViewColumn.Two, + preserveFocus: true, + })); + }; +} + +export function viewItemTree(ctx: CtxInit): Cmd { + const tdcp = new (class implements vscode.TextDocumentContentProvider { + readonly uri = vscode.Uri.parse("rust-analyzer-item-tree://viewItemTree/itemtree.rs"); + readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); + constructor() { + vscode.workspace.onDidChangeTextDocument( + this.onDidChangeTextDocument, + this, + ctx.subscriptions, + ); + vscode.window.onDidChangeActiveTextEditor( + this.onDidChangeActiveTextEditor, + this, + ctx.subscriptions, + ); + } + + private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { + if (isRustDocument(event.document)) { + // We need to order this after language server updates, but there's no API for that. + // Hence, good old sleep(). + void sleep(10).then(() => this.eventEmitter.fire(this.uri)); + } + } + private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) { + if (editor && isRustEditor(editor)) { + this.eventEmitter.fire(this.uri); + } + } + + async provideTextDocumentContent( + _uri: vscode.Uri, + ct: vscode.CancellationToken, + ): Promise<string> { + const rustEditor = ctx.activeRustEditor; + if (!rustEditor) return ""; + const client = ctx.client; + + const params = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier( + rustEditor.document, + ), + }; + return client.sendRequest(ra.viewItemTree, params, ct); + } + + get onDidChange(): vscode.Event<vscode.Uri> { + return this.eventEmitter.event; + } + })(); + + ctx.pushExtCleanup( + vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-item-tree", tdcp), + ); + + return async () => { + const document = await vscode.workspace.openTextDocument(tdcp.uri); + tdcp.eventEmitter.fire(tdcp.uri); + void (await vscode.window.showTextDocument(document, { + viewColumn: vscode.ViewColumn.Two, + preserveFocus: true, + })); + }; +} + +function crateGraph(ctx: CtxInit, full: boolean): Cmd { + return async () => { + const nodeModulesPath = vscode.Uri.file(path.join(ctx.extensionPath, "node_modules")); + + const panel = vscode.window.createWebviewPanel( + "rust-analyzer.crate-graph", + "rust-analyzer crate graph", + vscode.ViewColumn.Two, + { + enableScripts: true, + retainContextWhenHidden: true, + localResourceRoots: [nodeModulesPath], + }, + ); + const params = { + full: full, + }; + const client = ctx.client; + const dot = await client.sendRequest(ra.viewCrateGraph, params); + const uri = panel.webview.asWebviewUri(nodeModulesPath); + + const html = ` + <!DOCTYPE html> + <meta charset="utf-8"> + <head> + <style> + /* Fill the entire view */ + html, body { margin:0; padding:0; overflow:hidden } + svg { position:fixed; top:0; left:0; height:100%; width:100% } + + /* Disable the graphviz background and fill the polygons */ + .graph > polygon { display:none; } + :is(.node,.edge) polygon { fill: white; } + + /* Invert the line colours for dark themes */ + body:not(.vscode-light) .edge path { stroke: white; } + </style> + </head> + <body> + <script type="text/javascript" src="${uri}/d3/dist/d3.min.js"></script> + <script type="text/javascript" src="${uri}/@hpcc-js/wasm/dist/graphviz.umd.js"></script> + <script type="text/javascript" src="${uri}/d3-graphviz/build/d3-graphviz.min.js"></script> + <div id="graph"></div> + <script> + let dot = \`${dot}\`; + let graph = d3.select("#graph") + .graphviz({ useWorker: false, useSharedWorker: false }) + .fit(true) + .zoomScaleExtent([0.1, Infinity]) + .renderDot(dot); + + d3.select(window).on("click", (event) => { + if (event.ctrlKey) { + graph.resetZoom(d3.transition().duration(100)); + } + }); + d3.select(window).on("copy", (event) => { + event.clipboardData.setData("text/plain", dot); + event.preventDefault(); + }); + </script> + </body> + `; + + panel.webview.html = html; + }; +} + +export function viewCrateGraph(ctx: CtxInit): Cmd { + return crateGraph(ctx, false); +} + +export function viewFullCrateGraph(ctx: CtxInit): Cmd { + return crateGraph(ctx, true); +} + +// Opens the virtual file that will show the syntax tree +// +// The contents of the file come from the `TextDocumentContentProvider` +export function expandMacro(ctx: CtxInit): Cmd { + function codeFormat(expanded: ra.ExpandedMacro): string { + let result = `// Recursive expansion of ${expanded.name} macro\n`; + result += "// " + "=".repeat(result.length - 3); + result += "\n\n"; + result += expanded.expansion; + + return result; + } + + const tdcp = new (class implements vscode.TextDocumentContentProvider { + uri = vscode.Uri.parse("rust-analyzer-expand-macro://expandMacro/[EXPANSION].rs"); + eventEmitter = new vscode.EventEmitter<vscode.Uri>(); + async provideTextDocumentContent(_uri: vscode.Uri): Promise<string> { + const editor = vscode.window.activeTextEditor; + if (!editor) return ""; + const client = ctx.client; + + const position = editor.selection.active; + + const expanded = await client.sendRequest(ra.expandMacro, { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier( + editor.document, + ), + position, + }); + + if (expanded == null) return "Not available"; + + return codeFormat(expanded); + } + + get onDidChange(): vscode.Event<vscode.Uri> { + return this.eventEmitter.event; + } + })(); + + ctx.pushExtCleanup( + vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-expand-macro", tdcp), + ); + + return async () => { + const document = await vscode.workspace.openTextDocument(tdcp.uri); + tdcp.eventEmitter.fire(tdcp.uri); + return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true); + }; +} + +export function reloadWorkspace(ctx: CtxInit): Cmd { + return async () => ctx.client.sendRequest(ra.reloadWorkspace); +} + +export function rebuildProcMacros(ctx: CtxInit): Cmd { + return async () => ctx.client.sendRequest(ra.rebuildProcMacros); +} + +async function showReferencesImpl( + client: LanguageClient | undefined, + uri: string, + position: lc.Position, + locations: lc.Location[], +) { + if (client) { + await vscode.commands.executeCommand( + "editor.action.showReferences", + vscode.Uri.parse(uri), + client.protocol2CodeConverter.asPosition(position), + locations.map(client.protocol2CodeConverter.asLocation), + ); + } +} + +export function showReferences(ctx: CtxInit): Cmd { + return async (uri: string, position: lc.Position, locations: lc.Location[]) => { + await showReferencesImpl(ctx.client, uri, position, locations); + }; +} + +export function applyActionGroup(_ctx: CtxInit): Cmd { + return async (actions: { label: string; arguments: lc.CodeAction }[]) => { + const selectedAction = await vscode.window.showQuickPick(actions); + if (!selectedAction) return; + await vscode.commands.executeCommand( + "rust-analyzer.resolveCodeAction", + selectedAction.arguments, + ); + }; +} + +export function gotoLocation(ctx: CtxInit): Cmd { + return async (locationLink: lc.LocationLink) => { + const client = ctx.client; + const uri = client.protocol2CodeConverter.asUri(locationLink.targetUri); + let range = client.protocol2CodeConverter.asRange(locationLink.targetSelectionRange); + // collapse the range to a cursor position + range = range.with({ end: range.start }); + + await vscode.window.showTextDocument(uri, { selection: range }); + }; +} + +export function openDocs(ctx: CtxInit): Cmd { + return async () => { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + const client = ctx.client; + + const position = editor.selection.active; + const textDocument = { uri: editor.document.uri.toString() }; + + const docLinks = await client.sendRequest(ra.openDocs, { position, textDocument }); + log.debug(docLinks); + + let fileType = vscode.FileType.Unknown; + if (docLinks.local !== undefined) { + try { + fileType = (await vscode.workspace.fs.stat(vscode.Uri.parse(docLinks.local))).type; + } catch (e) { + log.debug("stat() threw error. Falling back to web version", e); + } + } + + let docLink = fileType & vscode.FileType.File ? docLinks.local : docLinks.web; + if (docLink) { + // instruct vscode to handle the vscode-remote link directly + if (docLink.startsWith("vscode-remote://")) { + docLink = docLink.replace("vscode-remote://", "vscode://vscode-remote/"); + } + const docUri = vscode.Uri.parse(docLink); + await vscode.env.openExternal(docUri); + } + }; +} + +export function openExternalDocs(ctx: CtxInit): Cmd { + return async () => { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + const client = ctx.client; + + const position = editor.selection.active; + const textDocument = { uri: editor.document.uri.toString() }; + + const docLinks = await client.sendRequest(ra.openDocs, { position, textDocument }); + + let docLink = docLinks.web; + if (docLink) { + // instruct vscode to handle the vscode-remote link directly + if (docLink.startsWith("vscode-remote://")) { + docLink = docLink.replace("vscode-remote://", "vscode://vscode-remote/"); + } + const docUri = vscode.Uri.parse(docLink); + await vscode.env.openExternal(docUri); + } + }; +} + +export function cancelFlycheck(ctx: CtxInit): Cmd { + return async () => { + await ctx.client.sendNotification(ra.cancelFlycheck); + }; +} + +export function clearFlycheck(ctx: CtxInit): Cmd { + return async () => { + await ctx.client.sendNotification(ra.clearFlycheck); + }; +} + +export function runFlycheck(ctx: CtxInit): Cmd { + return async () => { + const editor = ctx.activeRustEditor; + const client = ctx.client; + const params = editor ? { uri: editor.document.uri.toString() } : null; + + await client.sendNotification(ra.runFlycheck, { textDocument: params }); + }; +} + +export function resolveCodeAction(ctx: CtxInit): Cmd { + return async (params: lc.CodeAction) => { + const client = ctx.client; + params.command = undefined; + const item = await client.sendRequest(lc.CodeActionResolveRequest.type, params); + if (!item?.edit) { + return; + } + const itemEdit = item.edit; + // filter out all text edits and recreate the WorkspaceEdit without them so we can apply + // snippet edits on our own + const lcFileSystemEdit = { + ...itemEdit, + documentChanges: itemEdit.documentChanges?.filter((change) => "kind" in change), + }; + const fileSystemEdit = await client.protocol2CodeConverter.asWorkspaceEdit( + lcFileSystemEdit, + ); + await vscode.workspace.applyEdit(fileSystemEdit); + + // replace all text edits so that we can convert snippet text edits into `vscode.SnippetTextEdit`s + // FIXME: this is a workaround until vscode-languageclient supports doing the SnippeTextEdit conversion itself + // also need to carry the snippetTextDocumentEdits separately, since we can't retrieve them again using WorkspaceEdit.entries + const [workspaceTextEdit, snippetTextDocumentEdits] = asWorkspaceSnippetEdit(ctx, itemEdit); + await applySnippetWorkspaceEdit(workspaceTextEdit, snippetTextDocumentEdits); + if (item.command != null) { + await vscode.commands.executeCommand(item.command.command, item.command.arguments); + } + }; +} + +function asWorkspaceSnippetEdit( + ctx: CtxInit, + item: lc.WorkspaceEdit, +): [vscode.WorkspaceEdit, SnippetTextDocumentEdit[]] { + const client = ctx.client; + + // partially borrowed from https://github.com/microsoft/vscode-languageserver-node/blob/295aaa393fda8ecce110c38880a00466b9320e63/client/src/common/protocolConverter.ts#L1060-L1101 + const result = new vscode.WorkspaceEdit(); + + if (item.documentChanges) { + const snippetTextDocumentEdits: SnippetTextDocumentEdit[] = []; + + for (const change of item.documentChanges) { + if (lc.TextDocumentEdit.is(change)) { + const uri = client.protocol2CodeConverter.asUri(change.textDocument.uri); + const snippetTextEdits: (vscode.TextEdit | vscode.SnippetTextEdit)[] = []; + + for (const edit of change.edits) { + if ( + "insertTextFormat" in edit && + edit.insertTextFormat === lc.InsertTextFormat.Snippet + ) { + // is a snippet text edit + snippetTextEdits.push( + new vscode.SnippetTextEdit( + client.protocol2CodeConverter.asRange(edit.range), + new vscode.SnippetString(edit.newText), + ), + ); + } else { + // always as a text document edit + snippetTextEdits.push( + vscode.TextEdit.replace( + client.protocol2CodeConverter.asRange(edit.range), + edit.newText, + ), + ); + } + } + + snippetTextDocumentEdits.push([uri, snippetTextEdits]); + } + } + return [result, snippetTextDocumentEdits]; + } else { + // we don't handle WorkspaceEdit.changes since it's not relevant for code actions + return [result, []]; + } +} + +export function applySnippetWorkspaceEditCommand(_ctx: CtxInit): Cmd { + return async (edit: vscode.WorkspaceEdit) => { + await applySnippetWorkspaceEdit(edit, edit.entries()); + }; +} + +export function run(ctx: CtxInit): Cmd { + let prevRunnable: RunnableQuickPick | undefined; + + return async () => { + const item = await selectRunnable(ctx, prevRunnable); + if (!item) return; + + item.detail = "rerun"; + prevRunnable = item; + const task = await createTask(item.runnable, ctx.config); + return await vscode.tasks.executeTask(task); + }; +} + +export function peekTests(ctx: CtxInit): Cmd { + return async () => { + const editor = ctx.activeRustEditor; + if (!editor) return; + const client = ctx.client; + + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: "Looking for tests...", + cancellable: false, + }, + async (_progress, _token) => { + const uri = editor.document.uri.toString(); + const position = client.code2ProtocolConverter.asPosition(editor.selection.active); + + const tests = await client.sendRequest(ra.relatedTests, { + textDocument: { uri: uri }, + position: position, + }); + const locations: lc.Location[] = tests.map((it) => + lc.Location.create( + it.runnable.location!.targetUri, + it.runnable.location!.targetSelectionRange, + ), + ); + + await showReferencesImpl(client, uri, position, locations); + }, + ); + }; +} + +export function runSingle(ctx: CtxInit): Cmd { + return async (runnable: ra.Runnable) => { + const editor = ctx.activeRustEditor; + if (!editor) return; + + const task = await createTask(runnable, ctx.config); + task.group = vscode.TaskGroup.Build; + task.presentationOptions = { + reveal: vscode.TaskRevealKind.Always, + panel: vscode.TaskPanelKind.Dedicated, + clear: true, + }; + + return vscode.tasks.executeTask(task); + }; +} + +export function copyRunCommandLine(ctx: CtxInit) { + let prevRunnable: RunnableQuickPick | undefined; + return async () => { + const item = await selectRunnable(ctx, prevRunnable); + if (!item) return; + const args = createArgs(item.runnable); + const commandLine = ["cargo", ...args].join(" "); + await vscode.env.clipboard.writeText(commandLine); + await vscode.window.showInformationMessage("Cargo invocation copied to the clipboard."); + }; +} + +export function debug(ctx: CtxInit): Cmd { + let prevDebuggee: RunnableQuickPick | undefined; + + return async () => { + const item = await selectRunnable(ctx, prevDebuggee, true); + if (!item) return; + + item.detail = "restart"; + prevDebuggee = item; + return await startDebugSession(ctx, item.runnable); + }; +} + +export function debugSingle(ctx: CtxInit): Cmd { + return async (config: ra.Runnable) => { + await startDebugSession(ctx, config); + }; +} + +export function newDebugConfig(ctx: CtxInit): Cmd { + return async () => { + const item = await selectRunnable(ctx, undefined, true, false); + if (!item) return; + + await makeDebugConfig(ctx, item.runnable); + }; +} + +export function linkToCommand(_: Ctx): Cmd { + return async (commandId: string) => { + const link = LINKED_COMMANDS.get(commandId); + if (link) { + const { command, arguments: args = [] } = link; + await vscode.commands.executeCommand(command, ...args); + } + }; +} + +export function viewMemoryLayout(ctx: CtxInit): Cmd { + return async () => { + const editor = vscode.window.activeTextEditor; + if (!editor) return; + const client = ctx.client; + + const position = editor.selection.active; + const expanded = await client.sendRequest(ra.viewRecursiveMemoryLayout, { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), + position, + }); + + const document = vscode.window.createWebviewPanel( + "memory_layout", + "[Memory Layout]", + vscode.ViewColumn.Two, + { enableScripts: true }, + ); + + document.webview.html = `<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Document</title> + <style> + * { + box-sizing: border-box; + } + + body { + margin: 0; + overflow: hidden; + min-height: 100%; + height: 100vh; + padding: 32px; + position: relative; + display: block; + + background-color: var(--vscode-editor-background); + font-family: var(--vscode-editor-font-family); + font-size: var(--vscode-editor-font-size); + color: var(--vscode-editor-foreground); + } + + .container { + position: relative; + } + + .trans { + transition: all 0.2s ease-in-out; + } + + .grid { + height: 100%; + position: relative; + color: var(--vscode-commandCenter-activeBorder); + pointer-events: none; + } + + .grid-line { + position: absolute; + width: 100%; + height: 1px; + background-color: var(--vscode-commandCenter-activeBorder); + } + + #tooltip { + position: fixed; + display: none; + z-index: 1; + pointer-events: none; + padding: 4px 8px; + z-index: 2; + + color: var(--vscode-editorHoverWidget-foreground); + background-color: var(--vscode-editorHoverWidget-background); + border: 1px solid var(--vscode-editorHoverWidget-border); + } + + #tooltip b { + color: var(--vscode-editorInlayHint-typeForeground); + } + + #tooltip ul { + margin-left: 0; + padding-left: 20px; + } + + table { + position: absolute; + transform: rotateZ(90deg) rotateX(180deg); + transform-origin: top left; + border-collapse: collapse; + table-layout: fixed; + left: 48px; + top: 0; + max-height: calc(100vw - 64px - 48px); + z-index: 1; + } + + td { + border: 1px solid var(--vscode-focusBorder); + writing-mode: vertical-rl; + text-orientation: sideways-right; + + height: 80px; + } + + td p { + height: calc(100% - 16px); + width: calc(100% - 8px); + margin: 8px 4px; + display: inline-block; + transform: rotateY(180deg); + pointer-events: none; + overflow: hidden; + } + + td p * { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + display: inline-block; + height: 100%; + } + + td p b { + color: var(--vscode-editorInlayHint-typeForeground); + } + + td:hover { + background-color: var(--vscode-editor-hoverHighlightBackground); + } + + td:empty { + visibility: hidden; + border: 0; + } + </style> +</head> +<body> + <div id="tooltip"></div> +</body> +<script>(function() { + +const data = ${JSON.stringify(expanded)} + +if (!(data && data.nodes.length)) { + document.body.innerText = "Not Available" + return +} + +data.nodes.map(n => { + n.typename = n.typename.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', ' & quot; ').replaceAll("'", ''') + return n +}) + +let height = window.innerHeight - 64 + +addEventListener("resize", e => { + const new_height = window.innerHeight - 64 + height = new_height + container.classList.remove("trans") + table.classList.remove("trans") + locate() + setTimeout(() => { // give delay to redraw, annoying but needed + container.classList.add("trans") + table.classList.add("trans") + }, 0) +}) + +const container = document.createElement("div") +container.classList.add("container") +container.classList.add("trans") +document.body.appendChild(container) + +const tooltip = document.getElementById("tooltip") + +let y = 0 +let zoom = 1.0 + +const table = document.createElement("table") +table.classList.add("trans") +container.appendChild(table) +const rows = [] + +function node_t(idx, depth, offset) { + if (!rows[depth]) { + rows[depth] = { el: document.createElement("tr"), offset: 0 } + } + + if (rows[depth].offset < offset) { + const pad = document.createElement("td") + pad.colSpan = offset - rows[depth].offset + rows[depth].el.appendChild(pad) + rows[depth].offset += offset - rows[depth].offset + } + + const td = document.createElement("td") + td.innerHTML = '<p><span>' + data.nodes[idx].itemName + ':</span> <b>' + data.nodes[idx].typename + '</b></p>' + + td.colSpan = data.nodes[idx].size + + td.addEventListener("mouseover", e => { + const node = data.nodes[idx] + tooltip.innerHTML = node.itemName + ": <b>" + node.typename + "</b><br/>" + + "<ul>" + + "<li>size = " + node.size + "</li>" + + "<li>align = " + node.alignment + "</li>" + + "<li>field offset = " + node.offset + "</li>" + + "</ul>" + + "<i>double click to focus</i>" + + tooltip.style.display = "block" + }) + td.addEventListener("mouseleave", _ => tooltip.style.display = "none") + const total_offset = rows[depth].offset + td.addEventListener("dblclick", e => { + const node = data.nodes[idx] + zoom = data.nodes[0].size / node.size + y = -(total_offset) / data.nodes[0].size * zoom + x = 0 + locate() + }) + + rows[depth].el.appendChild(td) + rows[depth].offset += data.nodes[idx].size + + + if (data.nodes[idx].childrenStart != -1) { + for (let i = 0; i < data.nodes[idx].childrenLen; i++) { + if (data.nodes[data.nodes[idx].childrenStart + i].size) { + node_t(data.nodes[idx].childrenStart + i, depth + 1, offset + data.nodes[data.nodes[idx].childrenStart + i].offset) + } + } + } +} + +node_t(0, 0, 0) + +for (const row of rows) table.appendChild(row.el) + +const grid = document.createElement("div") +grid.classList.add("grid") +container.appendChild(grid) + +for (let i = 0; i < data.nodes[0].size / 8 + 1; i++) { + const el = document.createElement("div") + el.classList.add("grid-line") + el.style.top = (i / (data.nodes[0].size / 8) * 100) + "%" + el.innerText = i * 8 + grid.appendChild(el) +} + +addEventListener("mousemove", e => { + tooltip.style.top = e.clientY + 10 + "px" + tooltip.style.left = e.clientX + 10 + "px" +}) + +function locate() { + container.style.top = height * y + "px" + container.style.height = (height * zoom) + "px" + + table.style.width = container.style.height +} + +locate() + +})() +</script> +</html>`; + + ctx.pushExtCleanup(document); + }; +} + +export function toggleCheckOnSave(ctx: Ctx): Cmd { + return async () => { + await ctx.config.toggleCheckOnSave(); + ctx.refreshServerStatus(); + }; +} diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts new file mode 100644 index 00000000000..e676bc0826c --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -0,0 +1,528 @@ +import * as Is from "vscode-languageclient/lib/common/utils/is"; +import * as os from "os"; +import * as path from "path"; +import * as vscode from "vscode"; +import type { Env } from "./client"; +import { log } from "./util"; +import { expectNotUndefined, unwrapUndefinable } from "./undefinable"; +import type { JsonProject } from "./rust_project"; + +export type RunnableEnvCfgItem = { + mask?: string; + env: Record<string, string>; + platform?: string | string[]; +}; +export type RunnableEnvCfg = undefined | Record<string, string> | RunnableEnvCfgItem[]; + +export class Config { + readonly extensionId = "rust-lang.rust-analyzer"; + configureLang: vscode.Disposable | undefined; + + readonly rootSection = "rust-analyzer"; + private readonly requiresServerReloadOpts = [ + "cargo", + "procMacro", + "serverPath", + "server", + "files", + ].map((opt) => `${this.rootSection}.${opt}`); + + private readonly requiresWindowReloadOpts = ["testExplorer"].map( + (opt) => `${this.rootSection}.${opt}`, + ); + + readonly package: { + version: string; + releaseTag: string | null; + enableProposedApi: boolean | undefined; + } = vscode.extensions.getExtension(this.extensionId)!.packageJSON; + + readonly globalStorageUri: vscode.Uri; + + constructor(ctx: vscode.ExtensionContext) { + this.globalStorageUri = ctx.globalStorageUri; + this.discoveredWorkspaces = []; + vscode.workspace.onDidChangeConfiguration( + this.onDidChangeConfiguration, + this, + ctx.subscriptions, + ); + this.refreshLogging(); + this.configureLanguage(); + } + + dispose() { + this.configureLang?.dispose(); + } + + private refreshLogging() { + log.setEnabled(this.traceExtension ?? false); + log.info("Extension version:", this.package.version); + + const cfg = Object.entries(this.cfg).filter(([_, val]) => !(val instanceof Function)); + log.info("Using configuration", Object.fromEntries(cfg)); + } + + public discoveredWorkspaces: JsonProject[]; + + private async onDidChangeConfiguration(event: vscode.ConfigurationChangeEvent) { + this.refreshLogging(); + + this.configureLanguage(); + + const requiresWindowReloadOpt = this.requiresWindowReloadOpts.find((opt) => + event.affectsConfiguration(opt), + ); + + if (requiresWindowReloadOpt) { + const message = `Changing "${requiresWindowReloadOpt}" requires a window reload`; + const userResponse = await vscode.window.showInformationMessage(message, "Reload now"); + + if (userResponse) { + await vscode.commands.executeCommand("workbench.action.reloadWindow"); + } + } + + const requiresServerReloadOpt = this.requiresServerReloadOpts.find((opt) => + event.affectsConfiguration(opt), + ); + + if (!requiresServerReloadOpt) return; + + if (this.restartServerOnConfigChange) { + await vscode.commands.executeCommand("rust-analyzer.restartServer"); + return; + } + + const message = `Changing "${requiresServerReloadOpt}" requires a server restart`; + const userResponse = await vscode.window.showInformationMessage(message, "Restart now"); + + if (userResponse) { + const command = "rust-analyzer.restartServer"; + await vscode.commands.executeCommand(command); + } + } + + /** + * Sets up additional language configuration that's impossible to do via a + * separate language-configuration.json file. See [1] for more information. + * + * [1]: https://github.com/Microsoft/vscode/issues/11514#issuecomment-244707076 + */ + private configureLanguage() { + // Only need to dispose of the config if there's a change + if (this.configureLang) { + this.configureLang.dispose(); + this.configureLang = undefined; + } + + let onEnterRules: vscode.OnEnterRule[] = [ + { + // Carry indentation from the previous line + // if it's only whitespace + beforeText: /^\s+$/, + action: { indentAction: vscode.IndentAction.None }, + }, + { + // After the end of a function/field chain, + // with the semicolon on the same line + beforeText: /^\s+\..*;/, + action: { indentAction: vscode.IndentAction.Outdent }, + }, + { + // After the end of a function/field chain, + // with semicolon detached from the rest + beforeText: /^\s+;/, + previousLineText: /^\s+\..*/, + action: { indentAction: vscode.IndentAction.Outdent }, + }, + ]; + + if (this.typingContinueCommentsOnNewline) { + const indentAction = vscode.IndentAction.None; + + onEnterRules = [ + ...onEnterRules, + { + // Doc single-line comment + // e.g. ///| + beforeText: /^\s*\/{3}.*$/, + action: { indentAction, appendText: "/// " }, + }, + { + // Parent doc single-line comment + // e.g. //!| + beforeText: /^\s*\/{2}\!.*$/, + action: { indentAction, appendText: "//! " }, + }, + { + // Begins an auto-closed multi-line comment (standard or parent doc) + // e.g. /** | */ or /*! | */ + beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/, + afterText: /^\s*\*\/$/, + action: { + indentAction: vscode.IndentAction.IndentOutdent, + appendText: " * ", + }, + }, + { + // Begins a multi-line comment (standard or parent doc) + // e.g. /** ...| or /*! ...| + beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/, + action: { indentAction, appendText: " * " }, + }, + { + // Continues a multi-line comment + // e.g. * ...| + beforeText: /^(\ \ )*\ \*(\ ([^\*]|\*(?!\/))*)?$/, + action: { indentAction, appendText: "* " }, + }, + { + // Dedents after closing a multi-line comment + // e.g. */| + beforeText: /^(\ \ )*\ \*\/\s*$/, + action: { indentAction, removeText: 1 }, + }, + ]; + } + + this.configureLang = vscode.languages.setLanguageConfiguration("rust", { + onEnterRules, + }); + } + + // We don't do runtime config validation here for simplicity. More on stackoverflow: + // https://stackoverflow.com/questions/60135780/what-is-the-best-way-to-type-check-the-configuration-for-vscode-extension + + private get cfg(): vscode.WorkspaceConfiguration { + return vscode.workspace.getConfiguration(this.rootSection); + } + + /** + * Beware that postfix `!` operator erases both `null` and `undefined`. + * This is why the following doesn't work as expected: + * + * ```ts + * const nullableNum = vscode + * .workspace + * .getConfiguration + * .getConfiguration("rust-analyzer") + * .get<number | null>(path)!; + * + * // What happens is that type of `nullableNum` is `number` but not `null | number`: + * const fullFledgedNum: number = nullableNum; + * ``` + * So this getter handles this quirk by not requiring the caller to use postfix `!` + */ + private get<T>(path: string): T | undefined { + return prepareVSCodeConfig(this.cfg.get<T>(path)); + } + + get serverPath() { + return this.get<null | string>("server.path") ?? this.get<null | string>("serverPath"); + } + + get serverExtraEnv(): Env { + const extraEnv = + this.get<{ [key: string]: string | number } | null>("server.extraEnv") ?? {}; + return substituteVariablesInEnv( + Object.fromEntries( + Object.entries(extraEnv).map(([k, v]) => [ + k, + typeof v !== "string" ? v.toString() : v, + ]), + ), + ); + } + get checkOnSave() { + return this.get<boolean>("checkOnSave") ?? false; + } + async toggleCheckOnSave() { + const config = this.cfg.inspect<boolean>("checkOnSave") ?? { key: "checkOnSave" }; + let overrideInLanguage; + let target; + let value; + if ( + config.workspaceFolderValue !== undefined || + config.workspaceFolderLanguageValue !== undefined + ) { + target = vscode.ConfigurationTarget.WorkspaceFolder; + overrideInLanguage = config.workspaceFolderLanguageValue; + value = config.workspaceFolderValue || config.workspaceFolderLanguageValue; + } else if ( + config.workspaceValue !== undefined || + config.workspaceLanguageValue !== undefined + ) { + target = vscode.ConfigurationTarget.Workspace; + overrideInLanguage = config.workspaceLanguageValue; + value = config.workspaceValue || config.workspaceLanguageValue; + } else if (config.globalValue !== undefined || config.globalLanguageValue !== undefined) { + target = vscode.ConfigurationTarget.Global; + overrideInLanguage = config.globalLanguageValue; + value = config.globalValue || config.globalLanguageValue; + } else if (config.defaultValue !== undefined || config.defaultLanguageValue !== undefined) { + overrideInLanguage = config.defaultLanguageValue; + value = config.defaultValue || config.defaultLanguageValue; + } + await this.cfg.update("checkOnSave", !(value || false), target || null, overrideInLanguage); + } + + get traceExtension() { + return this.get<boolean>("trace.extension"); + } + + get discoverProjectRunner(): string | undefined { + return this.get<string | undefined>("discoverProjectRunner"); + } + + get problemMatcher(): string[] { + return this.get<string[]>("runnables.problemMatcher") || []; + } + + get cargoRunner() { + return this.get<string | undefined>("cargoRunner"); + } + + get testExplorer() { + return this.get<boolean | undefined>("testExplorer"); + } + + get runnablesExtraEnv() { + const item = this.get<any>("runnables.extraEnv") ?? this.get<any>("runnableEnv"); + if (!item) return item; + const fixRecord = (r: Record<string, any>) => { + for (const key in r) { + if (typeof r[key] !== "string") { + r[key] = String(r[key]); + } + } + }; + if (item instanceof Array) { + item.forEach((x) => fixRecord(x.env)); + } else { + fixRecord(item); + } + return item; + } + + get restartServerOnConfigChange() { + return this.get<boolean>("restartServerOnConfigChange"); + } + + get typingContinueCommentsOnNewline() { + return this.get<boolean>("typing.continueCommentsOnNewline"); + } + + get debug() { + let sourceFileMap = this.get<Record<string, string> | "auto">("debug.sourceFileMap"); + if (sourceFileMap !== "auto") { + // "/rustc/<id>" used by suggestions only. + const { ["/rustc/<id>"]: _, ...trimmed } = + this.get<Record<string, string>>("debug.sourceFileMap") ?? {}; + sourceFileMap = trimmed; + } + + return { + engine: this.get<string>("debug.engine"), + engineSettings: this.get<object>("debug.engineSettings") ?? {}, + openDebugPane: this.get<boolean>("debug.openDebugPane"), + sourceFileMap: sourceFileMap, + }; + } + + get hoverActions() { + return { + enable: this.get<boolean>("hover.actions.enable"), + implementations: this.get<boolean>("hover.actions.implementations.enable"), + references: this.get<boolean>("hover.actions.references.enable"), + run: this.get<boolean>("hover.actions.run.enable"), + debug: this.get<boolean>("hover.actions.debug.enable"), + gotoTypeDef: this.get<boolean>("hover.actions.gotoTypeDef.enable"), + }; + } + get previewRustcOutput() { + return this.get<boolean>("diagnostics.previewRustcOutput"); + } + + get useRustcErrorCode() { + return this.get<boolean>("diagnostics.useRustcErrorCode"); + } + + get showDependenciesExplorer() { + return this.get<boolean>("showDependenciesExplorer"); + } + + get statusBarClickAction() { + return this.get<string>("statusBar.clickAction"); + } +} + +// the optional `cb?` parameter is meant to be used to add additional +// key/value pairs to the VS Code configuration. This needed for, e.g., +// including a `rust-project.json` into the `linkedProjects` key as part +// of the configuration/InitializationParams _without_ causing VS Code +// configuration to be written out to workspace-level settings. This is +// undesirable behavior because rust-project.json files can be tens of +// thousands of lines of JSON, most of which is not meant for humans +// to interact with. +export function prepareVSCodeConfig<T>( + resp: T, + cb?: (key: Extract<keyof T, string>, res: { [key: string]: any }) => void, +): T { + if (Is.string(resp)) { + return substituteVSCodeVariableInString(resp) as T; + } else if (resp && Is.array<any>(resp)) { + return resp.map((val) => { + return prepareVSCodeConfig(val); + }) as T; + } else if (resp && typeof resp === "object") { + const res: { [key: string]: any } = {}; + for (const key in resp) { + const val = resp[key]; + res[key] = prepareVSCodeConfig(val); + if (cb) { + cb(key, res); + } + } + return res as T; + } + return resp; +} + +// FIXME: Merge this with `substituteVSCodeVariables` above +export function substituteVariablesInEnv(env: Env): Env { + const missingDeps = new Set<string>(); + // vscode uses `env:ENV_NAME` for env vars resolution, and it's easier + // to follow the same convention for our dependency tracking + const definedEnvKeys = new Set(Object.keys(env).map((key) => `env:${key}`)); + const envWithDeps = Object.fromEntries( + Object.entries(env).map(([key, value]) => { + const deps = new Set<string>(); + const depRe = new RegExp(/\${(?<depName>.+?)}/g); + let match = undefined; + while ((match = depRe.exec(value))) { + const depName = unwrapUndefinable(match.groups?.["depName"]); + deps.add(depName); + // `depName` at this point can have a form of `expression` or + // `prefix:expression` + if (!definedEnvKeys.has(depName)) { + missingDeps.add(depName); + } + } + return [`env:${key}`, { deps: [...deps], value }]; + }), + ); + + const resolved = new Set<string>(); + for (const dep of missingDeps) { + const match = /(?<prefix>.*?):(?<body>.+)/.exec(dep); + if (match) { + const { prefix, body } = match.groups!; + if (prefix === "env") { + const envName = unwrapUndefinable(body); + envWithDeps[dep] = { + value: process.env[envName] ?? "", + deps: [], + }; + resolved.add(dep); + } else { + // we can't handle other prefixes at the moment + // leave values as is, but still mark them as resolved + envWithDeps[dep] = { + value: "${" + dep + "}", + deps: [], + }; + resolved.add(dep); + } + } else { + envWithDeps[dep] = { + value: computeVscodeVar(dep) || "${" + dep + "}", + deps: [], + }; + } + } + const toResolve = new Set(Object.keys(envWithDeps)); + + let leftToResolveSize; + do { + leftToResolveSize = toResolve.size; + for (const key of toResolve) { + const item = unwrapUndefinable(envWithDeps[key]); + if (item.deps.every((dep) => resolved.has(dep))) { + item.value = item.value.replace(/\${(?<depName>.+?)}/g, (_wholeMatch, depName) => { + const item = unwrapUndefinable(envWithDeps[depName]); + return item.value; + }); + resolved.add(key); + toResolve.delete(key); + } + } + } while (toResolve.size > 0 && toResolve.size < leftToResolveSize); + + const resolvedEnv: Env = {}; + for (const key of Object.keys(env)) { + const item = unwrapUndefinable(envWithDeps[`env:${key}`]); + resolvedEnv[key] = item.value; + } + return resolvedEnv; +} + +const VarRegex = new RegExp(/\$\{(.+?)\}/g); +function substituteVSCodeVariableInString(val: string): string { + return val.replace(VarRegex, (substring: string, varName) => { + if (Is.string(varName)) { + return computeVscodeVar(varName) || substring; + } else { + return substring; + } + }); +} + +function computeVscodeVar(varName: string): string | null { + const workspaceFolder = () => { + const folders = vscode.workspace.workspaceFolders ?? []; + const folder = folders[0]; + // TODO: support for remote workspaces? + const fsPath: string = + folder === undefined + ? // no workspace opened + "" + : // could use currently opened document to detect the correct + // workspace. However, that would be determined by the document + // user has opened on Editor startup. Could lead to + // unpredictable workspace selection in practice. + // It's better to pick the first one + folder.uri.fsPath; + return fsPath; + }; + // https://code.visualstudio.com/docs/editor/variables-reference + const supportedVariables: { [k: string]: () => string } = { + workspaceFolder, + + workspaceFolderBasename: () => { + return path.basename(workspaceFolder()); + }, + + cwd: () => process.cwd(), + userHome: () => os.homedir(), + + // see + // https://github.com/microsoft/vscode/blob/08ac1bb67ca2459496b272d8f4a908757f24f56f/src/vs/workbench/api/common/extHostVariableResolverService.ts#L81 + // or + // https://github.com/microsoft/vscode/blob/29eb316bb9f154b7870eb5204ec7f2e7cf649bec/src/vs/server/node/remoteTerminalChannel.ts#L56 + execPath: () => process.env["VSCODE_EXEC_PATH"] ?? process.execPath, + + pathSeparator: () => path.sep, + }; + + if (varName in supportedVariables) { + const fn = expectNotUndefined( + supportedVariables[varName], + `${varName} should not be undefined here`, + ); + return fn(); + } else { + // return "${" + varName + "}"; + return null; + } +} diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts new file mode 100644 index 00000000000..f76dec2629a --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -0,0 +1,515 @@ +import * as vscode from "vscode"; +import * as lc from "vscode-languageclient/node"; +import * as ra from "./lsp_ext"; + +import { Config, prepareVSCodeConfig } from "./config"; +import { createClient } from "./client"; +import { + isDocumentInWorkspace, + isRustDocument, + isRustEditor, + LazyOutputChannel, + log, + type RustEditor, +} from "./util"; +import type { ServerStatusParams } from "./lsp_ext"; +import { + type Dependency, + type DependencyFile, + RustDependenciesProvider, + type DependencyId, +} from "./dependencies_provider"; +import { execRevealDependency } from "./commands"; +import { PersistentState } from "./persistent_state"; +import { bootstrap } from "./bootstrap"; +import type { RustAnalyzerExtensionApi } from "./main"; +import type { JsonProject } from "./rust_project"; +import { prepareTestExplorer } from "./test_explorer"; + +// We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if +// only those are in use. We use "Empty" to represent these scenarios +// (r-a still somewhat works with Live Share, because commands are tunneled to the host) + +export type Workspace = + | { kind: "Empty" } + | { + kind: "Workspace Folder"; + } + | { + kind: "Detached Files"; + files: vscode.TextDocument[]; + }; + +export function fetchWorkspace(): Workspace { + const folders = (vscode.workspace.workspaceFolders || []).filter( + (folder) => folder.uri.scheme === "file", + ); + const rustDocuments = vscode.workspace.textDocuments.filter((document) => + isRustDocument(document), + ); + + return folders.length === 0 + ? rustDocuments.length === 0 + ? { kind: "Empty" } + : { + kind: "Detached Files", + files: rustDocuments, + } + : { kind: "Workspace Folder" }; +} + +export type CommandFactory = { + enabled: (ctx: CtxInit) => Cmd; + disabled?: (ctx: Ctx) => Cmd; +}; + +export type CtxInit = Ctx & { + readonly client: lc.LanguageClient; +}; + +export class Ctx implements RustAnalyzerExtensionApi { + readonly statusBar: vscode.StatusBarItem; + config: Config; + readonly workspace: Workspace; + + private _client: lc.LanguageClient | undefined; + private _serverPath: string | undefined; + private traceOutputChannel: vscode.OutputChannel | undefined; + private testController: vscode.TestController | undefined; + private outputChannel: vscode.OutputChannel | undefined; + private clientSubscriptions: Disposable[]; + private state: PersistentState; + private commandFactories: Record<string, CommandFactory>; + private commandDisposables: Disposable[]; + private unlinkedFiles: vscode.Uri[]; + private _dependencies: RustDependenciesProvider | undefined; + private _treeView: vscode.TreeView<Dependency | DependencyFile | DependencyId> | undefined; + private lastStatus: ServerStatusParams | { health: "stopped" } = { health: "stopped" }; + + get client() { + return this._client; + } + + get treeView() { + return this._treeView; + } + + get dependencies() { + return this._dependencies; + } + + constructor( + readonly extCtx: vscode.ExtensionContext, + commandFactories: Record<string, CommandFactory>, + workspace: Workspace, + ) { + extCtx.subscriptions.push(this); + this.config = new Config(extCtx); + this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); + if (this.config.testExplorer) { + this.testController = vscode.tests.createTestController( + "rustAnalyzerTestController", + "Rust Analyzer test controller", + ); + } + this.workspace = workspace; + this.clientSubscriptions = []; + this.commandDisposables = []; + this.commandFactories = commandFactories; + this.unlinkedFiles = []; + this.state = new PersistentState(extCtx.globalState); + + this.updateCommands("disable"); + this.setServerStatus({ + health: "stopped", + }); + } + + dispose() { + this.config.dispose(); + this.statusBar.dispose(); + this.testController?.dispose(); + void this.disposeClient(); + this.commandDisposables.forEach((disposable) => disposable.dispose()); + } + + async onWorkspaceFolderChanges() { + const workspace = fetchWorkspace(); + if (workspace.kind === "Detached Files" && this.workspace.kind === "Detached Files") { + if (workspace.files !== this.workspace.files) { + if (this.client?.isRunning()) { + // Ideally we wouldn't need to tear down the server here, but currently detached files + // are only specified at server start + await this.stopAndDispose(); + await this.start(); + } + return; + } + } + if (workspace.kind === "Workspace Folder" && this.workspace.kind === "Workspace Folder") { + return; + } + if (workspace.kind === "Empty") { + await this.stopAndDispose(); + return; + } + if (this.client?.isRunning()) { + await this.restart(); + } + } + + private async getOrCreateClient() { + if (this.workspace.kind === "Empty") { + return; + } + + if (!this.traceOutputChannel) { + this.traceOutputChannel = new LazyOutputChannel("Rust Analyzer Language Server Trace"); + this.pushExtCleanup(this.traceOutputChannel); + } + if (!this.outputChannel) { + this.outputChannel = vscode.window.createOutputChannel("Rust Analyzer Language Server"); + this.pushExtCleanup(this.outputChannel); + } + + if (!this._client) { + this._serverPath = await bootstrap(this.extCtx, this.config, this.state).catch( + (err) => { + let message = "bootstrap error. "; + + message += + 'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically). '; + message += + 'To enable verbose logs use { "rust-analyzer.trace.extension": true }'; + + log.error("Bootstrap error", err); + throw new Error(message); + }, + ); + const newEnv = Object.assign({}, process.env, this.config.serverExtraEnv); + const run: lc.Executable = { + command: this._serverPath, + options: { env: newEnv }, + }; + const serverOptions = { + run, + debug: run, + }; + + let rawInitializationOptions = vscode.workspace.getConfiguration("rust-analyzer"); + if (this.config.discoverProjectRunner) { + const command = `${this.config.discoverProjectRunner}.discoverWorkspaceCommand`; + log.info(`running command: ${command}`); + const uris = vscode.workspace.textDocuments + .filter(isRustDocument) + .map((document) => document.uri); + const projects: JsonProject[] = await vscode.commands.executeCommand(command, uris); + this.setWorkspaces(projects); + } + + if (this.workspace.kind === "Detached Files") { + rawInitializationOptions = { + detachedFiles: this.workspace.files.map((file) => file.uri.fsPath), + ...rawInitializationOptions, + }; + } + + const initializationOptions = prepareVSCodeConfig( + rawInitializationOptions, + (key, obj) => { + // we only want to set discovered workspaces on the right key + // and if a workspace has been discovered. + if (key === "linkedProjects" && this.config.discoveredWorkspaces.length > 0) { + obj["linkedProjects"] = this.config.discoveredWorkspaces; + } + }, + ); + + this._client = await createClient( + this.traceOutputChannel, + this.outputChannel, + initializationOptions, + serverOptions, + this.config, + this.unlinkedFiles, + ); + this.pushClientCleanup( + this._client.onNotification(ra.serverStatus, (params) => + this.setServerStatus(params), + ), + ); + this.pushClientCleanup( + this._client.onNotification(ra.openServerLogs, () => { + this.outputChannel!.show(); + }), + ); + this.pushClientCleanup( + this._client.onNotification(ra.unindexedProject, async (params) => { + if (this.config.discoverProjectRunner) { + const command = `${this.config.discoverProjectRunner}.discoverWorkspaceCommand`; + log.info(`running command: ${command}`); + const uris = params.textDocuments.map((doc) => + vscode.Uri.parse(doc.uri, true), + ); + const projects: JsonProject[] = await vscode.commands.executeCommand( + command, + uris, + ); + this.setWorkspaces(projects); + await this.notifyRustAnalyzer(); + } + }), + ); + } + return this._client; + } + + async start() { + log.info("Starting language client"); + const client = await this.getOrCreateClient(); + if (!client) { + return; + } + await client.start(); + this.updateCommands(); + + if (this.testController) { + prepareTestExplorer(this, this.testController, client); + } + if (this.config.showDependenciesExplorer) { + this.prepareTreeDependenciesView(client); + } + } + + private prepareTreeDependenciesView(client: lc.LanguageClient) { + const ctxInit: CtxInit = { + ...this, + client: client, + }; + this._dependencies = new RustDependenciesProvider(ctxInit); + this._treeView = vscode.window.createTreeView("rustDependencies", { + treeDataProvider: this._dependencies, + showCollapseAll: true, + }); + + this.pushExtCleanup(this._treeView); + vscode.window.onDidChangeActiveTextEditor(async (e) => { + // we should skip documents that belong to the current workspace + if (this.shouldRevealDependency(e)) { + try { + await execRevealDependency(e); + } catch (reason) { + await vscode.window.showErrorMessage(`Dependency error: ${reason}`); + } + } + }); + + this.treeView?.onDidChangeVisibility(async (e) => { + if (e.visible) { + const activeEditor = vscode.window.activeTextEditor; + if (this.shouldRevealDependency(activeEditor)) { + try { + await execRevealDependency(activeEditor); + } catch (reason) { + await vscode.window.showErrorMessage(`Dependency error: ${reason}`); + } + } + } + }); + } + + private shouldRevealDependency(e: vscode.TextEditor | undefined): e is RustEditor { + return ( + e !== undefined && + isRustEditor(e) && + !isDocumentInWorkspace(e.document) && + (this.treeView?.visible || false) + ); + } + + async restart() { + // FIXME: We should re-use the client, that is ctx.deactivate() if none of the configs have changed + await this.stopAndDispose(); + await this.start(); + } + + async stop() { + if (!this._client) { + return; + } + log.info("Stopping language client"); + this.updateCommands("disable"); + await this._client.stop(); + } + + async stopAndDispose() { + if (!this._client) { + return; + } + log.info("Disposing language client"); + this.updateCommands("disable"); + await this.disposeClient(); + } + + private async disposeClient() { + this.clientSubscriptions?.forEach((disposable) => disposable.dispose()); + this.clientSubscriptions = []; + await this._client?.dispose(); + this._serverPath = undefined; + this._client = undefined; + } + + get activeRustEditor(): RustEditor | undefined { + const editor = vscode.window.activeTextEditor; + return editor && isRustEditor(editor) ? editor : undefined; + } + + get extensionPath(): string { + return this.extCtx.extensionPath; + } + + get subscriptions(): Disposable[] { + return this.extCtx.subscriptions; + } + + get serverPath(): string | undefined { + return this._serverPath; + } + + setWorkspaces(workspaces: JsonProject[]) { + this.config.discoveredWorkspaces = workspaces; + } + + async notifyRustAnalyzer(): Promise<void> { + // this is a workaround to avoid needing writing the `rust-project.json` into + // a workspace-level VS Code-specific settings folder. We'd like to keep the + // `rust-project.json` entirely in-memory. + await this.client?.sendNotification(lc.DidChangeConfigurationNotification.type, { + settings: "", + }); + } + + private updateCommands(forceDisable?: "disable") { + this.commandDisposables.forEach((disposable) => disposable.dispose()); + this.commandDisposables = []; + + const clientRunning = (!forceDisable && this._client?.isRunning()) ?? false; + const isClientRunning = function (_ctx: Ctx): _ctx is CtxInit { + return clientRunning; + }; + + for (const [name, factory] of Object.entries(this.commandFactories)) { + const fullName = `rust-analyzer.${name}`; + let callback; + if (isClientRunning(this)) { + // we asserted that `client` is defined + callback = factory.enabled(this); + } else if (factory.disabled) { + callback = factory.disabled(this); + } else { + callback = () => + vscode.window.showErrorMessage( + `command ${fullName} failed: rust-analyzer server is not running`, + ); + } + + this.commandDisposables.push(vscode.commands.registerCommand(fullName, callback)); + } + } + + setServerStatus(status: ServerStatusParams | { health: "stopped" }) { + this.lastStatus = status; + this.updateStatusBarItem(); + } + refreshServerStatus() { + this.updateStatusBarItem(); + } + private updateStatusBarItem() { + let icon = ""; + const status = this.lastStatus; + const statusBar = this.statusBar; + statusBar.show(); + statusBar.tooltip = new vscode.MarkdownString("", true); + statusBar.tooltip.isTrusted = true; + switch (status.health) { + case "ok": + statusBar.tooltip.appendText(status.message ?? "Ready"); + statusBar.color = undefined; + statusBar.backgroundColor = undefined; + if (this.config.statusBarClickAction === "stopServer") { + statusBar.command = "rust-analyzer.stopServer"; + } else { + statusBar.command = "rust-analyzer.openLogs"; + } + this.dependencies?.refresh(); + break; + case "warning": + if (status.message) { + statusBar.tooltip.appendText(status.message); + } + statusBar.color = new vscode.ThemeColor("statusBarItem.warningForeground"); + statusBar.backgroundColor = new vscode.ThemeColor( + "statusBarItem.warningBackground", + ); + statusBar.command = "rust-analyzer.openLogs"; + icon = "$(warning) "; + break; + case "error": + if (status.message) { + statusBar.tooltip.appendText(status.message); + } + statusBar.color = new vscode.ThemeColor("statusBarItem.errorForeground"); + statusBar.backgroundColor = new vscode.ThemeColor("statusBarItem.errorBackground"); + statusBar.command = "rust-analyzer.openLogs"; + icon = "$(error) "; + break; + case "stopped": + statusBar.tooltip.appendText("Server is stopped"); + statusBar.tooltip.appendMarkdown( + "\n\n[Start server](command:rust-analyzer.startServer)", + ); + statusBar.color = new vscode.ThemeColor("statusBarItem.warningForeground"); + statusBar.backgroundColor = new vscode.ThemeColor( + "statusBarItem.warningBackground", + ); + statusBar.command = "rust-analyzer.startServer"; + statusBar.text = "$(stop-circle) rust-analyzer"; + return; + } + if (statusBar.tooltip.value) { + statusBar.tooltip.appendMarkdown("\n\n---\n\n"); + } + statusBar.tooltip.appendMarkdown("\n\n[Open Logs](command:rust-analyzer.openLogs)"); + statusBar.tooltip.appendMarkdown( + `\n\n[${ + this.config.checkOnSave ? "Disable" : "Enable" + } Check on Save](command:rust-analyzer.toggleCheckOnSave)`, + ); + statusBar.tooltip.appendMarkdown( + "\n\n[Reload Workspace](command:rust-analyzer.reloadWorkspace)", + ); + statusBar.tooltip.appendMarkdown( + "\n\n[Rebuild Proc Macros](command:rust-analyzer.rebuildProcMacros)", + ); + statusBar.tooltip.appendMarkdown( + "\n\n[Restart server](command:rust-analyzer.restartServer)", + ); + statusBar.tooltip.appendMarkdown("\n\n[Stop server](command:rust-analyzer.stopServer)"); + if (!status.quiescent) icon = "$(sync~spin) "; + statusBar.text = `${icon}rust-analyzer`; + } + + pushExtCleanup(d: Disposable) { + this.extCtx.subscriptions.push(d); + } + + pushClientCleanup(d: Disposable) { + this.clientSubscriptions.push(d); + } +} + +export interface Disposable { + dispose(): void; +} + +export type Cmd = (...args: any[]) => unknown; diff --git a/src/tools/rust-analyzer/editors/code/src/debug.ts b/src/tools/rust-analyzer/editors/code/src/debug.ts new file mode 100644 index 00000000000..bad1f48de85 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/debug.ts @@ -0,0 +1,266 @@ +import * as os from "os"; +import * as vscode from "vscode"; +import * as path from "path"; +import type * as ra from "./lsp_ext"; + +import { Cargo, type ExecutableInfo, getRustcId, getSysroot } from "./toolchain"; +import type { Ctx } from "./ctx"; +import { prepareEnv } from "./run"; +import { unwrapUndefinable } from "./undefinable"; + +const debugOutput = vscode.window.createOutputChannel("Debug"); +type DebugConfigProvider = ( + config: ra.Runnable, + executable: string, + cargoWorkspace: string, + env: Record<string, string>, + sourceFileMap?: Record<string, string>, +) => vscode.DebugConfiguration; + +export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise<void> { + const scope = ctx.activeRustEditor?.document.uri; + if (!scope) return; + + const debugConfig = await getDebugConfiguration(ctx, runnable); + if (!debugConfig) return; + + const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope); + const configurations = wsLaunchSection.get<any[]>("configurations") || []; + + const index = configurations.findIndex((c) => c.name === debugConfig.name); + if (index !== -1) { + const answer = await vscode.window.showErrorMessage( + `Launch configuration '${debugConfig.name}' already exists!`, + "Cancel", + "Update", + ); + if (answer === "Cancel") return; + + configurations[index] = debugConfig; + } else { + configurations.push(debugConfig); + } + + await wsLaunchSection.update("configurations", configurations); +} + +export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promise<boolean> { + let debugConfig: vscode.DebugConfiguration | undefined = undefined; + let message = ""; + + const wsLaunchSection = vscode.workspace.getConfiguration("launch"); + const configurations = wsLaunchSection.get<any[]>("configurations") || []; + + const index = configurations.findIndex((c) => c.name === runnable.label); + if (-1 !== index) { + debugConfig = configurations[index]; + message = " (from launch.json)"; + debugOutput.clear(); + } else { + debugConfig = await getDebugConfiguration(ctx, runnable); + } + + if (!debugConfig) return false; + + debugOutput.appendLine(`Launching debug configuration${message}:`); + debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); + return vscode.debug.startDebugging(undefined, debugConfig); +} + +function createCommandLink(extensionId: string): string { + // do not remove the second quotes inside + // encodeURIComponent or it won't work + return `extension.open?${encodeURIComponent(`"${extensionId}"`)}`; +} + +async function getDebugConfiguration( + ctx: Ctx, + runnable: ra.Runnable, +): Promise<vscode.DebugConfiguration | undefined> { + const editor = ctx.activeRustEditor; + if (!editor) return; + + const knownEngines: Record<string, DebugConfigProvider> = { + "vadimcn.vscode-lldb": getCodeLldbDebugConfig, + "ms-vscode.cpptools": getCCppDebugConfig, + "webfreak.debug": getNativeDebugConfig, + }; + const debugOptions = ctx.config.debug; + + let debugEngine = null; + if (debugOptions.engine === "auto") { + for (var engineId in knownEngines) { + debugEngine = vscode.extensions.getExtension(engineId); + if (debugEngine) break; + } + } else if (debugOptions.engine) { + debugEngine = vscode.extensions.getExtension(debugOptions.engine); + } + + if (!debugEngine) { + const commandCCpp: string = createCommandLink("ms-vscode.cpptools"); + const commandCodeLLDB: string = createCommandLink("vadimcn.vscode-lldb"); + const commandNativeDebug: string = createCommandLink("webfreak.debug"); + + await vscode.window.showErrorMessage( + `Install [CodeLLDB](command:${commandCodeLLDB} "Open CodeLLDB")` + + `, [C/C++](command:${commandCCpp} "Open C/C++") ` + + `or [Native Debug](command:${commandNativeDebug} "Open Native Debug") for debugging.`, + ); + return; + } + + debugOutput.clear(); + if (ctx.config.debug.openDebugPane) { + debugOutput.show(true); + } + // folder exists or RA is not active. + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + const workspaceFolders = vscode.workspace.workspaceFolders!; + const isMultiFolderWorkspace = workspaceFolders.length > 1; + const firstWorkspace = workspaceFolders[0]; + const maybeWorkspace = + !isMultiFolderWorkspace || !runnable.args.workspaceRoot + ? firstWorkspace + : workspaceFolders.find((w) => runnable.args.workspaceRoot?.includes(w.uri.fsPath)) || + firstWorkspace; + + const workspace = unwrapUndefinable(maybeWorkspace); + const wsFolder = path.normalize(workspace.uri.fsPath); + const workspaceQualifier = isMultiFolderWorkspace ? `:${workspace.name}` : ""; + function simplifyPath(p: string): string { + // see https://github.com/rust-lang/rust-analyzer/pull/5513#issuecomment-663458818 for why this is needed + return path.normalize(p).replace(wsFolder, "${workspaceFolder" + workspaceQualifier + "}"); + } + + const env = prepareEnv(runnable, ctx.config.runnablesExtraEnv); + const { executable, workspace: cargoWorkspace } = await getDebugExecutableInfo(runnable, env); + let sourceFileMap = debugOptions.sourceFileMap; + if (sourceFileMap === "auto") { + // let's try to use the default toolchain + const [commitHash, sysroot] = await Promise.all([ + getRustcId(wsFolder), + getSysroot(wsFolder), + ]); + const rustlib = path.normalize(sysroot + "/lib/rustlib/src/rust"); + sourceFileMap = {}; + sourceFileMap[`/rustc/${commitHash}/`] = rustlib; + } + + const provider = unwrapUndefinable(knownEngines[debugEngine.id]); + const debugConfig = provider( + runnable, + simplifyPath(executable), + cargoWorkspace, + env, + sourceFileMap, + ); + if (debugConfig.type in debugOptions.engineSettings) { + const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; + for (var key in settingsMap) { + debugConfig[key] = settingsMap[key]; + } + } + + if (debugConfig.name === "run binary") { + // The LSP side: crates\rust-analyzer\src\main_loop\handlers.rs, + // fn to_lsp_runnable(...) with RunnableKind::Bin + debugConfig.name = `run ${path.basename(executable)}`; + } + + const cwd = debugConfig["cwd"]; + if (cwd) { + debugConfig["cwd"] = simplifyPath(cwd); + } + + return debugConfig; +} + +async function getDebugExecutableInfo( + runnable: ra.Runnable, + env: Record<string, string>, +): Promise<ExecutableInfo> { + const cargo = new Cargo(runnable.args.workspaceRoot || ".", debugOutput, env); + const executableInfo = await cargo.executableInfoFromArgs(runnable.args.cargoArgs); + + // if we are here, there were no compilation errors. + return executableInfo; +} + +function getCCppDebugConfig( + runnable: ra.Runnable, + executable: string, + cargoWorkspace: string, + env: Record<string, string>, + sourceFileMap?: Record<string, string>, +): vscode.DebugConfiguration { + return { + type: os.platform() === "win32" ? "cppvsdbg" : "cppdbg", + request: "launch", + name: runnable.label, + program: executable, + args: runnable.args.executableArgs, + cwd: cargoWorkspace || runnable.args.workspaceRoot, + sourceFileMap, + env, + // See https://github.com/rust-lang/rust-analyzer/issues/16901#issuecomment-2024486941 + osx: { + MIMode: "lldb", + }, + }; +} + +function getCodeLldbDebugConfig( + runnable: ra.Runnable, + executable: string, + cargoWorkspace: string, + env: Record<string, string>, + sourceFileMap?: Record<string, string>, +): vscode.DebugConfiguration { + return { + type: "lldb", + request: "launch", + name: runnable.label, + program: executable, + args: runnable.args.executableArgs, + cwd: cargoWorkspace || runnable.args.workspaceRoot, + sourceMap: sourceFileMap, + sourceLanguages: ["rust"], + env, + }; +} + +function getNativeDebugConfig( + runnable: ra.Runnable, + executable: string, + cargoWorkspace: string, + env: Record<string, string>, + _sourceFileMap?: Record<string, string>, +): vscode.DebugConfiguration { + return { + type: "gdb", + request: "launch", + name: runnable.label, + target: executable, + // See https://github.com/WebFreak001/code-debug/issues/359 + arguments: quote(runnable.args.executableArgs), + cwd: cargoWorkspace || runnable.args.workspaceRoot, + env, + valuesFormatting: "prettyPrinters", + }; +} + +// Based on https://github.com/ljharb/shell-quote/blob/main/quote.js +function quote(xs: string[]) { + return xs + .map(function (s) { + if (/["\s]/.test(s) && !/'/.test(s)) { + return "'" + s.replace(/(['\\])/g, "\\$1") + "'"; + } + if (/["'\s]/.test(s)) { + return '"' + s.replace(/(["\\$`!])/g, "\\$1") + '"'; + } + return s.replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}])/g, "$1\\$2"); + }) + .join(" "); +} diff --git a/src/tools/rust-analyzer/editors/code/src/dependencies_provider.ts b/src/tools/rust-analyzer/editors/code/src/dependencies_provider.ts new file mode 100644 index 00000000000..863ace07801 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/dependencies_provider.ts @@ -0,0 +1,154 @@ +import * as vscode from "vscode"; +import * as fspath from "path"; +import * as fs from "fs"; +import type { CtxInit } from "./ctx"; +import * as ra from "./lsp_ext"; +import type { FetchDependencyListResult } from "./lsp_ext"; +import { unwrapUndefinable } from "./undefinable"; + +export class RustDependenciesProvider + implements vscode.TreeDataProvider<Dependency | DependencyFile> +{ + dependenciesMap: { [id: string]: Dependency | DependencyFile }; + ctx: CtxInit; + + constructor(ctx: CtxInit) { + this.dependenciesMap = {}; + this.ctx = ctx; + } + + private _onDidChangeTreeData: vscode.EventEmitter< + Dependency | DependencyFile | undefined | null | void + > = new vscode.EventEmitter<Dependency | undefined | null | void>(); + + readonly onDidChangeTreeData: vscode.Event< + Dependency | DependencyFile | undefined | null | void + > = this._onDidChangeTreeData.event; + + getDependency(filePath: string): Dependency | DependencyFile | undefined { + return this.dependenciesMap[filePath.toLowerCase()]; + } + + contains(filePath: string): boolean { + return filePath.toLowerCase() in this.dependenciesMap; + } + + isInitialized(): boolean { + return Object.keys(this.dependenciesMap).length !== 0; + } + + refresh(): void { + this.dependenciesMap = {}; + this._onDidChangeTreeData.fire(); + } + + getParent?( + element: Dependency | DependencyFile, + ): vscode.ProviderResult<Dependency | DependencyFile> { + if (element instanceof Dependency) return undefined; + return element.parent; + } + + getTreeItem(element: Dependency | DependencyFile): vscode.TreeItem | Thenable<vscode.TreeItem> { + const dependenciesMap = this.dependenciesMap; + const elementId = element.id!; + if (elementId in dependenciesMap) { + const dependency = unwrapUndefinable(dependenciesMap[elementId]); + return dependency; + } + return element; + } + + getChildren( + element?: Dependency | DependencyFile, + ): vscode.ProviderResult<Dependency[] | DependencyFile[]> { + return new Promise((resolve, _reject) => { + if (!vscode.workspace.workspaceFolders) { + void vscode.window.showInformationMessage("No dependency in empty workspace"); + return Promise.resolve([]); + } + if (element) { + const files = fs.readdirSync(element.dependencyPath).map((fileName) => { + const filePath = fspath.join(element.dependencyPath, fileName); + const collapsibleState = fs.lstatSync(filePath).isDirectory() + ? vscode.TreeItemCollapsibleState.Collapsed + : vscode.TreeItemCollapsibleState.None; + const dep = new DependencyFile(fileName, filePath, element, collapsibleState); + this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep; + return dep; + }); + return resolve(files); + } else { + return resolve(this.getRootDependencies()); + } + }); + } + + private async getRootDependencies(): Promise<Dependency[]> { + const dependenciesResult: FetchDependencyListResult = await this.ctx.client.sendRequest( + ra.fetchDependencyList, + {}, + ); + const crates = dependenciesResult.crates; + + return crates + .map((crate) => { + const dep = this.toDep(crate.name || "unknown", crate.version || "", crate.path); + this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep; + return dep; + }) + .sort((a, b) => { + return a.label.localeCompare(b.label); + }); + } + + private toDep(moduleName: string, version: string, path: string): Dependency { + return new Dependency( + moduleName, + version, + vscode.Uri.parse(path).fsPath, + vscode.TreeItemCollapsibleState.Collapsed, + ); + } +} + +export class Dependency extends vscode.TreeItem { + constructor( + public override readonly label: string, + private version: string, + readonly dependencyPath: string, + public override readonly collapsibleState: vscode.TreeItemCollapsibleState, + ) { + super(label, collapsibleState); + this.resourceUri = vscode.Uri.file(dependencyPath); + this.id = this.resourceUri.fsPath.toLowerCase(); + this.description = this.version; + if (this.version) { + this.tooltip = `${this.label}-${this.version}`; + } else { + this.tooltip = this.label; + } + } +} + +export class DependencyFile extends vscode.TreeItem { + constructor( + override readonly label: string, + readonly dependencyPath: string, + readonly parent: Dependency | DependencyFile, + public override readonly collapsibleState: vscode.TreeItemCollapsibleState, + ) { + super(vscode.Uri.file(dependencyPath), collapsibleState); + this.id = this.resourceUri!.fsPath.toLowerCase(); + const isDir = fs.lstatSync(this.resourceUri!.fsPath).isDirectory(); + if (!isDir) { + this.command = { + command: "vscode.open", + title: "Open File", + arguments: [this.resourceUri], + }; + } + } +} + +export type DependencyId = { id: string }; diff --git a/src/tools/rust-analyzer/editors/code/src/diagnostics.ts b/src/tools/rust-analyzer/editors/code/src/diagnostics.ts new file mode 100644 index 00000000000..e31a1cdcef9 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/diagnostics.ts @@ -0,0 +1,220 @@ +import * as anser from "anser"; +import * as vscode from "vscode"; +import { + type ProviderResult, + Range, + type TextEditorDecorationType, + ThemeColor, + window, +} from "vscode"; +import type { Ctx } from "./ctx"; +import { unwrapUndefinable } from "./undefinable"; + +export const URI_SCHEME = "rust-analyzer-diagnostics-view"; + +export class TextDocumentProvider implements vscode.TextDocumentContentProvider { + private _onDidChange = new vscode.EventEmitter<vscode.Uri>(); + + public constructor(private readonly ctx: Ctx) {} + + get onDidChange(): vscode.Event<vscode.Uri> { + return this._onDidChange.event; + } + + triggerUpdate(uri: vscode.Uri) { + if (uri.scheme === URI_SCHEME) { + this._onDidChange.fire(uri); + } + } + + dispose() { + this._onDidChange.dispose(); + } + + async provideTextDocumentContent(uri: vscode.Uri): Promise<string> { + const contents = getRenderedDiagnostic(this.ctx, uri); + return anser.ansiToText(contents); + } +} + +function getRenderedDiagnostic(ctx: Ctx, uri: vscode.Uri): string { + const diags = ctx.client?.diagnostics?.get(vscode.Uri.parse(uri.fragment, true)); + if (!diags) { + return "Unable to find original rustc diagnostic"; + } + + const diag = diags[parseInt(uri.query)]; + if (!diag) { + return "Unable to find original rustc diagnostic"; + } + const rendered = (diag as unknown as { data?: { rendered?: string } }).data?.rendered; + + if (!rendered) { + return "Unable to find original rustc diagnostic"; + } + + return rendered; +} + +interface AnserStyle { + fg: string; + bg: string; + fg_truecolor: string; + bg_truecolor: string; + decorations: Array<anser.DecorationName>; +} + +export class AnsiDecorationProvider implements vscode.Disposable { + private _decorationTypes = new Map<AnserStyle, TextEditorDecorationType>(); + + public constructor(private readonly ctx: Ctx) {} + + dispose(): void { + for (const decorationType of this._decorationTypes.values()) { + decorationType.dispose(); + } + + this._decorationTypes.clear(); + } + + async provideDecorations(editor: vscode.TextEditor) { + if (editor.document.uri.scheme !== URI_SCHEME) { + return; + } + + const decorations = (await this._getDecorations(editor.document.uri)) || []; + for (const [decorationType, ranges] of decorations) { + editor.setDecorations(decorationType, ranges); + } + } + + private _getDecorations( + uri: vscode.Uri, + ): ProviderResult<[TextEditorDecorationType, Range[]][]> { + const stringContents = getRenderedDiagnostic(this.ctx, uri); + const lines = stringContents.split("\n"); + + const result = new Map<TextEditorDecorationType, Range[]>(); + // Populate all known decoration types in the result. This forces any + // lingering decorations to be cleared if the text content changes to + // something without ANSI codes for a given decoration type. + for (const decorationType of this._decorationTypes.values()) { + result.set(decorationType, []); + } + + for (const [lineNumber, line] of lines.entries()) { + const totalEscapeLength = 0; + + // eslint-disable-next-line camelcase + const parsed = anser.ansiToJson(line, { use_classes: true }); + + let offset = 0; + + for (const span of parsed) { + const { content, ...style } = span; + + const range = new Range( + lineNumber, + offset - totalEscapeLength, + lineNumber, + offset + content.length - totalEscapeLength, + ); + + offset += content.length; + + const decorationType = this._getDecorationType(style); + + if (!result.has(decorationType)) { + result.set(decorationType, []); + } + + result.get(decorationType)!.push(range); + } + } + + return [...result]; + } + + private _getDecorationType(style: AnserStyle): TextEditorDecorationType { + let decorationType = this._decorationTypes.get(style); + + if (decorationType) { + return decorationType; + } + + const fontWeight = style.decorations.find((s) => s === "bold"); + const fontStyle = style.decorations.find((s) => s === "italic"); + const textDecoration = style.decorations.find((s) => s === "underline"); + + decorationType = window.createTextEditorDecorationType({ + backgroundColor: AnsiDecorationProvider._convertColor(style.bg, style.bg_truecolor), + color: AnsiDecorationProvider._convertColor(style.fg, style.fg_truecolor), + fontWeight, + fontStyle, + textDecoration, + }); + + this._decorationTypes.set(style, decorationType); + + return decorationType; + } + + // NOTE: This could just be a kebab-case to camelCase conversion, but I think it's + // a short enough list to just write these by hand + static readonly _anserToThemeColor: Record<string, ThemeColor> = { + "ansi-black": "ansiBlack", + "ansi-white": "ansiWhite", + "ansi-red": "ansiRed", + "ansi-green": "ansiGreen", + "ansi-yellow": "ansiYellow", + "ansi-blue": "ansiBlue", + "ansi-magenta": "ansiMagenta", + "ansi-cyan": "ansiCyan", + + "ansi-bright-black": "ansiBrightBlack", + "ansi-bright-white": "ansiBrightWhite", + "ansi-bright-red": "ansiBrightRed", + "ansi-bright-green": "ansiBrightGreen", + "ansi-bright-yellow": "ansiBrightYellow", + "ansi-bright-blue": "ansiBrightBlue", + "ansi-bright-magenta": "ansiBrightMagenta", + "ansi-bright-cyan": "ansiBrightCyan", + }; + + private static _convertColor( + color?: string, + truecolor?: string, + ): ThemeColor | string | undefined { + if (!color) { + return undefined; + } + + if (color === "ansi-truecolor") { + if (!truecolor) { + return undefined; + } + return `rgb(${truecolor})`; + } + + const paletteMatch = color.match(/ansi-palette-(.+)/); + if (paletteMatch) { + const paletteColor = paletteMatch[1]; + // anser won't return both the RGB and the color name at the same time, + // so just fake a single foreground control char with the palette number: + const spans = anser.ansiToJson(`\x1b[38;5;${paletteColor}m`); + const span = unwrapUndefinable(spans[1]); + const rgb = span.fg; + + if (rgb) { + return `rgb(${rgb})`; + } + } + + const themeColor = AnsiDecorationProvider._anserToThemeColor[color]; + if (themeColor) { + return new ThemeColor("terminal." + themeColor); + } + + return undefined; + } +} diff --git a/src/tools/rust-analyzer/editors/code/src/lang_client.ts b/src/tools/rust-analyzer/editors/code/src/lang_client.ts new file mode 100644 index 00000000000..09d64efc048 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/lang_client.ts @@ -0,0 +1,26 @@ +import * as lc from "vscode-languageclient/node"; +import * as vscode from "vscode"; + +export class RaLanguageClient extends lc.LanguageClient { + override handleFailedRequest<T>( + type: lc.MessageSignature, + token: vscode.CancellationToken | undefined, + error: any, + defaultValue: T, + showNotification?: boolean | undefined, + ): T { + const showError = vscode.workspace + .getConfiguration("rust-analyzer") + .get("showRequestFailedErrorNotification"); + if ( + !showError && + error instanceof lc.ResponseError && + error.code === lc.ErrorCodes.InternalError + ) { + // Don't show notification for internal errors, these are emitted by r-a when a request fails. + showNotification = false; + } + + return super.handleFailedRequest(type, token, error, defaultValue, showNotification); + } +} diff --git a/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts new file mode 100644 index 00000000000..9a7a4aae959 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts @@ -0,0 +1,271 @@ +/** + * This file mirrors `crates/rust-analyzer/src/lsp_ext.rs` declarations. + */ + +import * as lc from "vscode-languageclient"; + +// rust-analyzer overrides + +export const hover = new lc.RequestType< + HoverParams, + (lc.Hover & { actions: CommandLinkGroup[] }) | null, + void +>(lc.HoverRequest.method); +export type HoverParams = { position: lc.Position | lc.Range } & Omit<lc.HoverParams, "position">; + +export type CommandLink = { + /** + * A tooltip for the command, when represented in the UI. + */ + tooltip?: string; +} & lc.Command; +export type CommandLinkGroup = { + title?: string; + commands: CommandLink[]; +}; + +// rust-analyzer extensions + +export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>( + "rust-analyzer/analyzerStatus", +); +export const cancelFlycheck = new lc.NotificationType0("rust-analyzer/cancelFlycheck"); +export const clearFlycheck = new lc.NotificationType0("rust-analyzer/clearFlycheck"); +export const expandMacro = new lc.RequestType<ExpandMacroParams, ExpandedMacro | null, void>( + "rust-analyzer/expandMacro", +); +export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage"); +export const openServerLogs = new lc.NotificationType0("rust-analyzer/openServerLogs"); +export const relatedTests = new lc.RequestType<lc.TextDocumentPositionParams, TestInfo[], void>( + "rust-analyzer/relatedTests", +); +export const reloadWorkspace = new lc.RequestType0<null, void>("rust-analyzer/reloadWorkspace"); +export const rebuildProcMacros = new lc.RequestType0<null, void>("rust-analyzer/rebuildProcMacros"); + +export const runFlycheck = new lc.NotificationType<{ + textDocument: lc.TextDocumentIdentifier | null; +}>("rust-analyzer/runFlycheck"); +export const shuffleCrateGraph = new lc.RequestType0<null, void>("rust-analyzer/shuffleCrateGraph"); +export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>( + "rust-analyzer/syntaxTree", +); +export const viewCrateGraph = new lc.RequestType<ViewCrateGraphParams, string, void>( + "rust-analyzer/viewCrateGraph", +); +export const viewFileText = new lc.RequestType<lc.TextDocumentIdentifier, string, void>( + "rust-analyzer/viewFileText", +); +export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>( + "rust-analyzer/viewHir", +); +export const viewMir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>( + "rust-analyzer/viewMir", +); +export const interpretFunction = new lc.RequestType<lc.TextDocumentPositionParams, string, void>( + "rust-analyzer/interpretFunction", +); +export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>( + "rust-analyzer/viewItemTree", +); + +export type DiscoverTestParams = { testId?: string | undefined }; +export type RunTestParams = { + include?: string[] | undefined; + exclude?: string[] | undefined; +}; +export type TestItem = { + id: string; + label: string; + kind: "package" | "module" | "test"; + canResolveChildren: boolean; + parent?: string | undefined; + textDocument?: lc.TextDocumentIdentifier | undefined; + range?: lc.Range | undefined; + runnable?: Runnable | undefined; +}; +export type DiscoverTestResults = { + tests: TestItem[]; + scope: string[] | undefined; + scopeFile: lc.TextDocumentIdentifier[] | undefined; +}; +export type TestState = + | { tag: "failed"; message: string } + | { tag: "passed" } + | { tag: "started" } + | { tag: "enqueued" } + | { tag: "skipped" }; +export type ChangeTestStateParams = { testId: string; state: TestState }; +export const discoverTest = new lc.RequestType<DiscoverTestParams, DiscoverTestResults, void>( + "experimental/discoverTest", +); +export const discoveredTests = new lc.NotificationType<DiscoverTestResults>( + "experimental/discoveredTests", +); +export const runTest = new lc.RequestType<RunTestParams, void, void>("experimental/runTest"); +export const abortRunTest = new lc.NotificationType0("experimental/abortRunTest"); +export const endRunTest = new lc.NotificationType0("experimental/endRunTest"); +export const appendOutputToRunTest = new lc.NotificationType<string>( + "experimental/appendOutputToRunTest", +); +export const changeTestState = new lc.NotificationType<ChangeTestStateParams>( + "experimental/changeTestState", +); + +export type AnalyzerStatusParams = { textDocument?: lc.TextDocumentIdentifier }; + +export interface FetchDependencyListParams {} + +export interface FetchDependencyListResult { + crates: { + name?: string; + version?: string; + path: string; + }[]; +} + +export const fetchDependencyList = new lc.RequestType< + FetchDependencyListParams, + FetchDependencyListResult, + void +>("rust-analyzer/fetchDependencyList"); + +export interface FetchDependencyGraphParams {} + +export interface FetchDependencyGraphResult { + crates: { + name: string; + version: string; + path: string; + }[]; +} + +export const fetchDependencyGraph = new lc.RequestType< + FetchDependencyGraphParams, + FetchDependencyGraphResult, + void +>("rust-analyzer/fetchDependencyGraph"); + +export type ExpandMacroParams = { + textDocument: lc.TextDocumentIdentifier; + position: lc.Position; +}; +export type ExpandedMacro = { + name: string; + expansion: string; +}; +export type TestInfo = { runnable: Runnable }; +export type SyntaxTreeParams = { + textDocument: lc.TextDocumentIdentifier; + range: lc.Range | null; +}; +export type ViewCrateGraphParams = { full: boolean }; +export type ViewItemTreeParams = { textDocument: lc.TextDocumentIdentifier }; + +// experimental extensions + +export const joinLines = new lc.RequestType<JoinLinesParams, lc.TextEdit[], void>( + "experimental/joinLines", +); +export const matchingBrace = new lc.RequestType<MatchingBraceParams, lc.Position[], void>( + "experimental/matchingBrace", +); +export const moveItem = new lc.RequestType<MoveItemParams, lc.TextEdit[], void>( + "experimental/moveItem", +); +export const onEnter = new lc.RequestType<lc.TextDocumentPositionParams, lc.TextEdit[], void>( + "experimental/onEnter", +); +export const openCargoToml = new lc.RequestType<OpenCargoTomlParams, lc.Location, void>( + "experimental/openCargoToml", +); +export interface DocsUrls { + local?: string; + web?: string; +} +export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, DocsUrls, void>( + "experimental/externalDocs", +); +export const parentModule = new lc.RequestType< + lc.TextDocumentPositionParams, + lc.LocationLink[] | null, + void +>("experimental/parentModule"); +export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>( + "experimental/runnables", +); +export const serverStatus = new lc.NotificationType<ServerStatusParams>( + "experimental/serverStatus", +); +export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>("experimental/ssr"); +export const viewRecursiveMemoryLayout = new lc.RequestType< + lc.TextDocumentPositionParams, + RecursiveMemoryLayout | null, + void +>("rust-analyzer/viewRecursiveMemoryLayout"); + +export type JoinLinesParams = { + textDocument: lc.TextDocumentIdentifier; + ranges: lc.Range[]; +}; +export type MatchingBraceParams = { + textDocument: lc.TextDocumentIdentifier; + positions: lc.Position[]; +}; +export type MoveItemParams = { + textDocument: lc.TextDocumentIdentifier; + range: lc.Range; + direction: Direction; +}; +export type Direction = "Up" | "Down"; +export type OpenCargoTomlParams = { + textDocument: lc.TextDocumentIdentifier; +}; +export type Runnable = { + label: string; + location?: lc.LocationLink; + kind: "cargo"; + args: { + workspaceRoot?: string; + cargoArgs: string[]; + cargoExtraArgs: string[]; + executableArgs: string[]; + expectTest?: boolean; + overrideCargo?: string; + }; +}; +export type RunnablesParams = { + textDocument: lc.TextDocumentIdentifier; + position: lc.Position | null; +}; +export type ServerStatusParams = { + health: "ok" | "warning" | "error"; + quiescent: boolean; + message?: string; +}; +export type SsrParams = { + query: string; + parseOnly: boolean; + textDocument: lc.TextDocumentIdentifier; + position: lc.Position; + selections: readonly lc.Range[]; +}; + +export type RecursiveMemoryLayoutNode = { + item_name: string; + typename: string; + size: number; + alignment: number; + offset: number; + parent_idx: number; + children_start: number; + children_len: number; +}; +export type RecursiveMemoryLayout = { + nodes: RecursiveMemoryLayoutNode[]; +}; + +export const unindexedProject = new lc.NotificationType<UnindexedProjectParams>( + "rust-analyzer/unindexedProject", +); + +export type UnindexedProjectParams = { textDocuments: lc.TextDocumentIdentifier[] }; diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts new file mode 100644 index 00000000000..0af58fd7812 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/main.ts @@ -0,0 +1,216 @@ +import * as vscode from "vscode"; +import * as lc from "vscode-languageclient/node"; + +import * as commands from "./commands"; +import { type CommandFactory, Ctx, fetchWorkspace } from "./ctx"; +import * as diagnostics from "./diagnostics"; +import { activateTaskProvider } from "./tasks"; +import { setContextValue } from "./util"; +import type { JsonProject } from "./rust_project"; + +const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; + +// This API is not stable and may break in between minor releases. +export interface RustAnalyzerExtensionApi { + readonly client?: lc.LanguageClient; + + setWorkspaces(workspaces: JsonProject[]): void; + notifyRustAnalyzer(): Promise<void>; +} + +export async function deactivate() { + await setContextValue(RUST_PROJECT_CONTEXT_NAME, undefined); +} + +export async function activate( + context: vscode.ExtensionContext, +): Promise<RustAnalyzerExtensionApi> { + checkConflictingExtensions(); + + const ctx = new Ctx(context, createCommands(), fetchWorkspace()); + // VS Code doesn't show a notification when an extension fails to activate + // so we do it ourselves. + const api = await activateServer(ctx).catch((err) => { + void vscode.window.showErrorMessage( + `Cannot activate rust-analyzer extension: ${err.message}`, + ); + throw err; + }); + await setContextValue(RUST_PROJECT_CONTEXT_NAME, true); + return api; +} + +async function activateServer(ctx: Ctx): Promise<RustAnalyzerExtensionApi> { + if (ctx.workspace.kind === "Workspace Folder") { + ctx.pushExtCleanup(activateTaskProvider(ctx.config)); + } + + const diagnosticProvider = new diagnostics.TextDocumentProvider(ctx); + ctx.pushExtCleanup( + vscode.workspace.registerTextDocumentContentProvider( + diagnostics.URI_SCHEME, + diagnosticProvider, + ), + ); + + const decorationProvider = new diagnostics.AnsiDecorationProvider(ctx); + ctx.pushExtCleanup(decorationProvider); + + async function decorateVisibleEditors(document: vscode.TextDocument) { + for (const editor of vscode.window.visibleTextEditors) { + if (document === editor.document) { + await decorationProvider.provideDecorations(editor); + } + } + } + + vscode.workspace.onDidChangeTextDocument( + async (event) => await decorateVisibleEditors(event.document), + null, + ctx.subscriptions, + ); + vscode.workspace.onDidOpenTextDocument(decorateVisibleEditors, null, ctx.subscriptions); + vscode.window.onDidChangeActiveTextEditor( + async (editor) => { + if (editor) { + diagnosticProvider.triggerUpdate(editor.document.uri); + await decorateVisibleEditors(editor.document); + } + }, + null, + ctx.subscriptions, + ); + vscode.window.onDidChangeVisibleTextEditors( + async (visibleEditors) => { + for (const editor of visibleEditors) { + diagnosticProvider.triggerUpdate(editor.document.uri); + await decorationProvider.provideDecorations(editor); + } + }, + null, + ctx.subscriptions, + ); + + vscode.workspace.onDidChangeWorkspaceFolders( + async (_) => ctx.onWorkspaceFolderChanges(), + null, + ctx.subscriptions, + ); + vscode.workspace.onDidChangeConfiguration( + async (_) => { + await ctx.client?.sendNotification(lc.DidChangeConfigurationNotification.type, { + settings: "", + }); + }, + null, + ctx.subscriptions, + ); + + await ctx.start(); + return ctx; +} + +function createCommands(): Record<string, CommandFactory> { + return { + onEnter: { + enabled: commands.onEnter, + disabled: (_) => () => vscode.commands.executeCommand("default:type", { text: "\n" }), + }, + restartServer: { + enabled: (ctx) => async () => { + await ctx.restart(); + }, + disabled: (ctx) => async () => { + await ctx.start(); + }, + }, + startServer: { + enabled: (ctx) => async () => { + await ctx.start(); + }, + disabled: (ctx) => async () => { + await ctx.start(); + }, + }, + stopServer: { + enabled: (ctx) => async () => { + // FIXME: We should re-use the client, that is ctx.deactivate() if none of the configs have changed + await ctx.stopAndDispose(); + ctx.setServerStatus({ + health: "stopped", + }); + }, + disabled: (_) => async () => {}, + }, + + analyzerStatus: { enabled: commands.analyzerStatus }, + memoryUsage: { enabled: commands.memoryUsage }, + shuffleCrateGraph: { enabled: commands.shuffleCrateGraph }, + reloadWorkspace: { enabled: commands.reloadWorkspace }, + rebuildProcMacros: { enabled: commands.rebuildProcMacros }, + matchingBrace: { enabled: commands.matchingBrace }, + joinLines: { enabled: commands.joinLines }, + parentModule: { enabled: commands.parentModule }, + syntaxTree: { enabled: commands.syntaxTree }, + viewHir: { enabled: commands.viewHir }, + viewMir: { enabled: commands.viewMir }, + interpretFunction: { enabled: commands.interpretFunction }, + viewFileText: { enabled: commands.viewFileText }, + viewItemTree: { enabled: commands.viewItemTree }, + viewCrateGraph: { enabled: commands.viewCrateGraph }, + viewFullCrateGraph: { enabled: commands.viewFullCrateGraph }, + expandMacro: { enabled: commands.expandMacro }, + run: { enabled: commands.run }, + copyRunCommandLine: { enabled: commands.copyRunCommandLine }, + debug: { enabled: commands.debug }, + newDebugConfig: { enabled: commands.newDebugConfig }, + openDocs: { enabled: commands.openDocs }, + openExternalDocs: { enabled: commands.openExternalDocs }, + openCargoToml: { enabled: commands.openCargoToml }, + peekTests: { enabled: commands.peekTests }, + moveItemUp: { enabled: commands.moveItemUp }, + moveItemDown: { enabled: commands.moveItemDown }, + cancelFlycheck: { enabled: commands.cancelFlycheck }, + clearFlycheck: { enabled: commands.clearFlycheck }, + runFlycheck: { enabled: commands.runFlycheck }, + ssr: { enabled: commands.ssr }, + serverVersion: { enabled: commands.serverVersion }, + viewMemoryLayout: { enabled: commands.viewMemoryLayout }, + toggleCheckOnSave: { enabled: commands.toggleCheckOnSave }, + // Internal commands which are invoked by the server. + applyActionGroup: { enabled: commands.applyActionGroup }, + applySnippetWorkspaceEdit: { enabled: commands.applySnippetWorkspaceEditCommand }, + debugSingle: { enabled: commands.debugSingle }, + gotoLocation: { enabled: commands.gotoLocation }, + linkToCommand: { enabled: commands.linkToCommand }, + resolveCodeAction: { enabled: commands.resolveCodeAction }, + runSingle: { enabled: commands.runSingle }, + showReferences: { enabled: commands.showReferences }, + triggerParameterHints: { enabled: commands.triggerParameterHints }, + openLogs: { enabled: commands.openLogs }, + revealDependency: { enabled: commands.revealDependency }, + }; +} + +function checkConflictingExtensions() { + if (vscode.extensions.getExtension("rust-lang.rust")) { + vscode.window + .showWarningMessage( + `You have both the rust-analyzer (rust-lang.rust-analyzer) and Rust (rust-lang.rust) ` + + "plugins enabled. These are known to conflict and cause various functions of " + + "both plugins to not work correctly. You should disable one of them.", + "Got it", + ) + .then(() => {}, console.error); + } + + if (vscode.extensions.getExtension("panicbit.cargo")) { + vscode.window + .showWarningMessage( + `You have both the rust-analyzer (rust-lang.rust-analyzer) and Cargo (panicbit.cargo) plugins enabled, ` + + 'you can disable it or set {"cargo.automaticCheck": false} in settings.json to avoid invoking cargo twice', + "Got it", + ) + .then(() => {}, console.error); + } +} diff --git a/src/tools/rust-analyzer/editors/code/src/nullable.ts b/src/tools/rust-analyzer/editors/code/src/nullable.ts new file mode 100644 index 00000000000..e973e162907 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/nullable.ts @@ -0,0 +1,19 @@ +export type NotNull<T> = T extends null ? never : T; + +export type Nullable<T> = T | null; + +function isNotNull<T>(input: Nullable<T>): input is NotNull<T> { + return input !== null; +} + +function expectNotNull<T>(input: Nullable<T>, msg: string): NotNull<T> { + if (isNotNull(input)) { + return input; + } + + throw new TypeError(msg); +} + +export function unwrapNullable<T>(input: Nullable<T>): NotNull<T> { + return expectNotNull(input, `unwrapping \`null\``); +} diff --git a/src/tools/rust-analyzer/editors/code/src/persistent_state.ts b/src/tools/rust-analyzer/editors/code/src/persistent_state.ts new file mode 100644 index 00000000000..cebd16a3c90 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/persistent_state.ts @@ -0,0 +1,20 @@ +import type * as vscode from "vscode"; +import { log } from "./util"; + +export class PersistentState { + constructor(private readonly globalState: vscode.Memento) { + const { serverVersion } = this; + log.info("PersistentState:", { serverVersion }); + } + + /** + * Version of the extension that installed the server. + * Used to check if we need to run patchelf again on NixOS. + */ + get serverVersion(): string | undefined { + return this.globalState.get("serverVersion"); + } + async updateServerVersion(value: string | undefined) { + await this.globalState.update("serverVersion", value); + } +} diff --git a/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts new file mode 100644 index 00000000000..4470689cd8c --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/run.ts @@ -0,0 +1,225 @@ +import * as vscode from "vscode"; +import type * as lc from "vscode-languageclient"; +import * as ra from "./lsp_ext"; +import * as tasks from "./tasks"; + +import type { CtxInit } from "./ctx"; +import { makeDebugConfig } from "./debug"; +import type { Config, RunnableEnvCfg, RunnableEnvCfgItem } from "./config"; +import { unwrapUndefinable } from "./undefinable"; +import type { LanguageClient } from "vscode-languageclient/node"; +import type { RustEditor } from "./util"; + +const quickPickButtons = [ + { iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configuration." }, +]; + +export async function selectRunnable( + ctx: CtxInit, + prevRunnable?: RunnableQuickPick, + debuggeeOnly = false, + showButtons: boolean = true, +): Promise<RunnableQuickPick | undefined> { + const editor = ctx.activeRustEditor; + if (!editor) return; + + // show a placeholder while we get the runnables from the server + const quickPick = vscode.window.createQuickPick(); + quickPick.title = "Select Runnable"; + if (showButtons) { + quickPick.buttons = quickPickButtons; + } + quickPick.items = [{ label: "Looking for runnables..." }]; + quickPick.activeItems = []; + quickPick.show(); + + const runnables = await getRunnables(ctx.client, editor, prevRunnable, debuggeeOnly); + + if (runnables.length === 0) { + // it is the debug case, run always has at least 'cargo check ...' + // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables + await vscode.window.showErrorMessage("There's no debug target!"); + quickPick.dispose(); + return; + } + + // clear the list before we hook up listeners to avoid invoking them + // if the user happens to accept the placeholder item + quickPick.items = []; + + return await populateAndGetSelection( + quickPick as vscode.QuickPick<RunnableQuickPick>, + runnables, + ctx, + showButtons, + ); +} + +export class RunnableQuickPick implements vscode.QuickPickItem { + public label: string; + public description?: string | undefined; + public detail?: string | undefined; + public picked?: boolean | undefined; + + constructor(public runnable: ra.Runnable) { + this.label = runnable.label; + } +} + +export function prepareEnv( + runnable: ra.Runnable, + runnableEnvCfg: RunnableEnvCfg, +): Record<string, string> { + const env: Record<string, string> = { RUST_BACKTRACE: "short" }; + + if (runnable.args.expectTest) { + env["UPDATE_EXPECT"] = "1"; + } + + Object.assign(env, process.env as { [key: string]: string }); + const platform = process.platform; + + const checkPlatform = (it: RunnableEnvCfgItem) => { + if (it.platform) { + const platforms = Array.isArray(it.platform) ? it.platform : [it.platform]; + return platforms.indexOf(platform) >= 0; + } + return true; + }; + + if (runnableEnvCfg) { + if (Array.isArray(runnableEnvCfg)) { + for (const it of runnableEnvCfg) { + const masked = !it.mask || new RegExp(it.mask).test(runnable.label); + if (masked && checkPlatform(it)) { + Object.assign(env, it.env); + } + } + } else { + Object.assign(env, runnableEnvCfg); + } + } + + return env; +} + +export async function createTask(runnable: ra.Runnable, config: Config): Promise<vscode.Task> { + if (runnable.kind !== "cargo") { + // rust-analyzer supports only one kind, "cargo" + // do not use tasks.TASK_TYPE here, these are completely different meanings. + + throw `Unexpected runnable kind: ${runnable.kind}`; + } + + const args = createArgs(runnable); + + const definition: tasks.CargoTaskDefinition = { + type: tasks.TASK_TYPE, + command: unwrapUndefinable(args[0]), // run, test, etc... + args: args.slice(1), + cwd: runnable.args.workspaceRoot || ".", + env: prepareEnv(runnable, config.runnablesExtraEnv), + overrideCargo: runnable.args.overrideCargo, + }; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate() + const task = await tasks.buildRustTask( + target, + definition, + runnable.label, + config.problemMatcher, + config.cargoRunner, + true, + ); + + task.presentationOptions.clear = true; + // Sadly, this doesn't prevent focus stealing if the terminal is currently + // hidden, and will become revealed due to task execution. + task.presentationOptions.focus = false; + + return task; +} + +export function createArgs(runnable: ra.Runnable): string[] { + const args = [...runnable.args.cargoArgs]; // should be a copy! + if (runnable.args.cargoExtraArgs) { + args.push(...runnable.args.cargoExtraArgs); // Append user-specified cargo options. + } + if (runnable.args.executableArgs.length > 0) { + args.push("--", ...runnable.args.executableArgs); + } + return args; +} + +async function getRunnables( + client: LanguageClient, + editor: RustEditor, + prevRunnable?: RunnableQuickPick, + debuggeeOnly = false, +): Promise<RunnableQuickPick[]> { + const textDocument: lc.TextDocumentIdentifier = { + uri: editor.document.uri.toString(), + }; + + const runnables = await client.sendRequest(ra.runnables, { + textDocument, + position: client.code2ProtocolConverter.asPosition(editor.selection.active), + }); + const items: RunnableQuickPick[] = []; + if (prevRunnable) { + items.push(prevRunnable); + } + for (const r of runnables) { + if (prevRunnable && JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)) { + continue; + } + + if (debuggeeOnly && (r.label.startsWith("doctest") || r.label.startsWith("cargo"))) { + continue; + } + items.push(new RunnableQuickPick(r)); + } + + return items; +} + +async function populateAndGetSelection( + quickPick: vscode.QuickPick<RunnableQuickPick>, + runnables: RunnableQuickPick[], + ctx: CtxInit, + showButtons: boolean, +): Promise<RunnableQuickPick | undefined> { + return new Promise((resolve) => { + const disposables: vscode.Disposable[] = []; + const close = (result?: RunnableQuickPick) => { + resolve(result); + disposables.forEach((d) => d.dispose()); + }; + disposables.push( + quickPick.onDidHide(() => close()), + quickPick.onDidAccept(() => close(quickPick.selectedItems[0] as RunnableQuickPick)), + quickPick.onDidTriggerButton(async (_button) => { + const runnable = unwrapUndefinable( + quickPick.activeItems[0] as RunnableQuickPick, + ).runnable; + await makeDebugConfig(ctx, runnable); + close(); + }), + quickPick.onDidChangeActive((activeList) => { + if (showButtons && activeList.length > 0) { + const active = unwrapUndefinable(activeList[0]); + if (active.label.startsWith("cargo")) { + // save button makes no sense for `cargo test` or `cargo check` + quickPick.buttons = []; + } else if (quickPick.buttons.length === 0) { + quickPick.buttons = quickPickButtons; + } + } + }), + quickPick, + ); + // populate the list with the actual runnables + quickPick.items = runnables; + }); +} diff --git a/src/tools/rust-analyzer/editors/code/src/rust_project.ts b/src/tools/rust-analyzer/editors/code/src/rust_project.ts new file mode 100644 index 00000000000..c983874fc00 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/rust_project.ts @@ -0,0 +1,110 @@ +export interface JsonProject { + /// Path to the sysroot directory. + /// + /// The sysroot is where rustc looks for the + /// crates that are built-in to rust, such as + /// std. + /// + /// https://doc.rust-lang.org/rustc/command-line-arguments.html#--sysroot-override-the-system-root + /// + /// To see the current value of sysroot, you + /// can query rustc: + /// + /// ``` + /// $ rustc --print sysroot + /// /Users/yourname/.rustup/toolchains/stable-x86_64-apple-darwin + /// ``` + sysroot?: string; + /// Path to the directory with *source code* of + /// sysroot crates. + /// + /// By default, this is `lib/rustlib/src/rust/library` + /// relative to the sysroot. + /// + /// It should point to the directory where std, + /// core, and friends can be found: + /// + /// https://github.com/rust-lang/rust/tree/master/library. + /// + /// If provided, rust-analyzer automatically adds + /// dependencies on sysroot crates. Conversely, + /// if you omit this path, you can specify sysroot + /// dependencies yourself and, for example, have + /// several different "sysroots" in one graph of + /// crates. + sysroot_src?: string; + /// The set of crates comprising the current + /// project. Must include all transitive + /// dependencies as well as sysroot crate (libstd, + /// libcore and such). + crates: Crate[]; +} + +export interface Crate { + /// Optional crate name used for display purposes, + /// without affecting semantics. See the `deps` + /// key for semantically-significant crate names. + display_name?: string; + /// Path to the root module of the crate. + root_module: string; + /// Edition of the crate. + edition: "2015" | "2018" | "2021"; + /// Dependencies + deps: Dep[]; + /// Should this crate be treated as a member of + /// current "workspace". + /// + /// By default, inferred from the `root_module` + /// (members are the crates which reside inside + /// the directory opened in the editor). + /// + /// Set this to `false` for things like standard + /// library and 3rd party crates to enable + /// performance optimizations (rust-analyzer + /// assumes that non-member crates don't change). + is_workspace_member?: boolean; + /// Optionally specify the (super)set of `.rs` + /// files comprising this crate. + /// + /// By default, rust-analyzer assumes that only + /// files under `root_module.parent` can belong + /// to a crate. `include_dirs` are included + /// recursively, unless a subdirectory is in + /// `exclude_dirs`. + /// + /// Different crates can share the same `source`. + /// + /// If two crates share an `.rs` file in common, + /// they *must* have the same `source`. + /// rust-analyzer assumes that files from one + /// source can't refer to files in another source. + source?: { + include_dirs: string[]; + exclude_dirs: string[]; + }; + /// The set of cfgs activated for a given crate, like + /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`. + cfg: string[]; + /// Target triple for this Crate. + /// + /// Used when running `rustc --print cfg` + /// to get target-specific cfgs. + target?: string; + /// Environment variables, used for + /// the `env!` macro + env: { [key: string]: string }; + + /// Whether the crate is a proc-macro crate. + is_proc_macro: boolean; + /// For proc-macro crates, path to compiled + /// proc-macro (.so file). + proc_macro_dylib_path?: string; +} + +export interface Dep { + /// Index of a crate in the `crates` array. + crate: number; + /// Name as should appear in the (implicit) + /// `extern crate name` declaration. + name: string; +} diff --git a/src/tools/rust-analyzer/editors/code/src/snippets.ts b/src/tools/rust-analyzer/editors/code/src/snippets.ts new file mode 100644 index 00000000000..b3982bdf2be --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/snippets.ts @@ -0,0 +1,143 @@ +import * as vscode from "vscode"; + +import { assert } from "./util"; +import { unwrapUndefinable } from "./undefinable"; + +export type SnippetTextDocumentEdit = [vscode.Uri, (vscode.TextEdit | vscode.SnippetTextEdit)[]]; + +export async function applySnippetWorkspaceEdit( + edit: vscode.WorkspaceEdit, + editEntries: SnippetTextDocumentEdit[], +) { + if (editEntries.length === 1) { + const [uri, edits] = unwrapUndefinable(editEntries[0]); + const editor = await editorFromUri(uri); + if (editor) { + edit.set(uri, removeLeadingWhitespace(editor, edits)); + await vscode.workspace.applyEdit(edit); + } + return; + } + for (const [uri, edits] of editEntries) { + const editor = await editorFromUri(uri); + if (editor) { + await editor.edit((builder) => { + for (const indel of edits) { + assert( + !(indel instanceof vscode.SnippetTextEdit), + `bad ws edit: snippet received with multiple edits: ${JSON.stringify( + edit, + )}`, + ); + builder.replace(indel.range, indel.newText); + } + }); + } + } +} + +async function editorFromUri(uri: vscode.Uri): Promise<vscode.TextEditor | undefined> { + if (vscode.window.activeTextEditor?.document.uri !== uri) { + // `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed + await vscode.window.showTextDocument(uri, {}); + } + return vscode.window.visibleTextEditors.find( + (it) => it.document.uri.toString() === uri.toString(), + ); +} + +export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) { + const edit = new vscode.WorkspaceEdit(); + const snippetEdits = toSnippetTextEdits(edits); + edit.set(editor.document.uri, removeLeadingWhitespace(editor, snippetEdits)); + await vscode.workspace.applyEdit(edit); +} + +function hasSnippet(snip: string): boolean { + const m = snip.match(/\$\d+|\{\d+:[^}]*\}/); + return m != null; +} + +function toSnippetTextEdits( + edits: vscode.TextEdit[], +): (vscode.TextEdit | vscode.SnippetTextEdit)[] { + return edits.map((textEdit) => { + // Note: text edits without any snippets are returned as-is instead of + // being wrapped in a SnippetTextEdit, as otherwise it would be + // treated as if it had a tab stop at the end. + if (hasSnippet(textEdit.newText)) { + return new vscode.SnippetTextEdit( + textEdit.range, + new vscode.SnippetString(textEdit.newText), + ); + } else { + return textEdit; + } + }); +} + +/** + * Removes the leading whitespace from snippet edits, so as to not double up + * on indentation. + * + * Snippet edits by default adjust any multi-line snippets to match the + * indentation of the line to insert at. Unfortunately, we (the server) also + * include the required indentation to match what we line insert at, so we end + * up doubling up the indentation. Since there isn't any way to tell vscode to + * not fixup indentation for us, we instead opt to remove the indentation and + * then let vscode add it back in. + * + * This assumes that the source snippet text edits have the required + * indentation, but that's okay as even without this workaround and the problem + * to workaround, those snippet edits would already be inserting at the wrong + * indentation. + */ +function removeLeadingWhitespace( + editor: vscode.TextEditor, + edits: (vscode.TextEdit | vscode.SnippetTextEdit)[], +) { + return edits.map((edit) => { + if (edit instanceof vscode.SnippetTextEdit) { + const snippetEdit: vscode.SnippetTextEdit = edit; + const firstLineEnd = snippetEdit.snippet.value.indexOf("\n"); + + if (firstLineEnd !== -1) { + // Is a multi-line snippet, remove the indentation which + // would be added back in by vscode. + const startLine = editor.document.lineAt(snippetEdit.range.start.line); + const leadingWhitespace = getLeadingWhitespace( + startLine.text, + 0, + startLine.firstNonWhitespaceCharacterIndex, + ); + + const [firstLine, rest] = splitAt(snippetEdit.snippet.value, firstLineEnd + 1); + const unindentedLines = rest + .split("\n") + .map((line) => line.replace(leadingWhitespace, "")) + .join("\n"); + + snippetEdit.snippet.value = firstLine + unindentedLines; + } + + return snippetEdit; + } else { + return edit; + } + }); +} + +// based on https://github.com/microsoft/vscode/blob/main/src/vs/base/common/strings.ts#L284 +function getLeadingWhitespace(str: string, start: number = 0, end: number = str.length): string { + for (let i = start; i < end; i++) { + const chCode = str.charCodeAt(i); + if (chCode !== " ".charCodeAt(0) && chCode !== " ".charCodeAt(0)) { + return str.substring(start, i); + } + } + return str.substring(start, end); +} + +function splitAt(str: string, index: number): [string, string] { + return [str.substring(0, index), str.substring(index)]; +} diff --git a/src/tools/rust-analyzer/editors/code/src/tasks.ts b/src/tools/rust-analyzer/editors/code/src/tasks.ts new file mode 100644 index 00000000000..2b3abc5d65f --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/tasks.ts @@ -0,0 +1,160 @@ +import * as vscode from "vscode"; +import * as toolchain from "./toolchain"; +import type { Config } from "./config"; +import { log } from "./util"; +import { unwrapUndefinable } from "./undefinable"; + +// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and +// our configuration should be compatible with it so use the same key. +export const TASK_TYPE = "cargo"; + +export const TASK_SOURCE = "rust"; + +export interface CargoTaskDefinition extends vscode.TaskDefinition { + // The cargo command, such as "run" or "check". + command: string; + // Additional arguments passed to the cargo command. + args?: string[]; + // The working directory to run the cargo command in. + cwd?: string; + // The shell environment. + env?: { [key: string]: string }; + // Override the cargo executable name, such as + // "my_custom_cargo_bin". + overrideCargo?: string; +} + +class RustTaskProvider implements vscode.TaskProvider { + private readonly config: Config; + + constructor(config: Config) { + this.config = config; + } + + async provideTasks(): Promise<vscode.Task[]> { + // Detect Rust tasks. Currently we do not do any actual detection + // of tasks (e.g. aliases in .cargo/config) and just return a fixed + // set of tasks that always exist. These tasks cannot be removed in + // tasks.json - only tweaked. + + const defs = [ + { command: "build", group: vscode.TaskGroup.Build }, + { command: "check", group: vscode.TaskGroup.Build }, + { command: "clippy", group: vscode.TaskGroup.Build }, + { command: "test", group: vscode.TaskGroup.Test }, + { command: "clean", group: vscode.TaskGroup.Clean }, + { command: "run", group: undefined }, + ]; + + const tasks: vscode.Task[] = []; + for (const workspaceTarget of vscode.workspace.workspaceFolders || []) { + for (const def of defs) { + const vscodeTask = await buildRustTask( + workspaceTarget, + { type: TASK_TYPE, command: def.command }, + `cargo ${def.command}`, + this.config.problemMatcher, + this.config.cargoRunner, + ); + vscodeTask.group = def.group; + tasks.push(vscodeTask); + } + } + + return tasks; + } + + async resolveTask(task: vscode.Task): Promise<vscode.Task | undefined> { + // VSCode calls this for every cargo task in the user's tasks.json, + // we need to inform VSCode how to execute that command by creating + // a ShellExecution for it. + + const definition = task.definition as CargoTaskDefinition; + + if (definition.type === TASK_TYPE) { + return await buildRustTask( + task.scope, + definition, + task.name, + this.config.problemMatcher, + this.config.cargoRunner, + ); + } + + return undefined; + } +} + +export async function buildRustTask( + scope: vscode.WorkspaceFolder | vscode.TaskScope | undefined, + definition: CargoTaskDefinition, + name: string, + problemMatcher: string[], + customRunner?: string, + throwOnError: boolean = false, +): Promise<vscode.Task> { + const exec = await cargoToExecution(definition, customRunner, throwOnError); + + return new vscode.Task( + definition, + // scope can sometimes be undefined. in these situations we default to the workspace taskscope as + // recommended by the official docs: https://code.visualstudio.com/api/extension-guides/task-provider#task-provider) + scope ?? vscode.TaskScope.Workspace, + name, + TASK_SOURCE, + exec, + problemMatcher, + ); +} + +async function cargoToExecution( + definition: CargoTaskDefinition, + customRunner: string | undefined, + throwOnError: boolean, +): Promise<vscode.ProcessExecution | vscode.ShellExecution> { + if (customRunner) { + const runnerCommand = `${customRunner}.buildShellExecution`; + + try { + const runnerArgs = { + kind: TASK_TYPE, + args: definition.args, + cwd: definition.cwd, + env: definition.env, + }; + const customExec = await vscode.commands.executeCommand(runnerCommand, runnerArgs); + if (customExec) { + if (customExec instanceof vscode.ShellExecution) { + return customExec; + } else { + log.debug("Invalid cargo ShellExecution", customExec); + throw "Invalid cargo ShellExecution."; + } + } + // fallback to default processing + } catch (e) { + if (throwOnError) throw `Cargo runner '${customRunner}' failed! ${e}`; + // fallback to default processing + } + } + + // Check whether we must use a user-defined substitute for cargo. + // Split on spaces to allow overrides like "wrapper cargo". + const cargoPath = await toolchain.cargoPath(); + const cargoCommand = definition.overrideCargo?.split(" ") ?? [cargoPath]; + + const args = [definition.command].concat(definition.args ?? []); + const fullCommand = [...cargoCommand, ...args]; + + const processName = unwrapUndefinable(fullCommand[0]); + + return new vscode.ProcessExecution(processName, fullCommand.slice(1), { + cwd: definition.cwd, + env: definition.env, + }); +} + +export function activateTaskProvider(config: Config): vscode.Disposable { + const provider = new RustTaskProvider(config); + return vscode.tasks.registerTaskProvider(TASK_TYPE, provider); +} diff --git a/src/tools/rust-analyzer/editors/code/src/test_explorer.ts b/src/tools/rust-analyzer/editors/code/src/test_explorer.ts new file mode 100644 index 00000000000..de41d2a57ec --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/test_explorer.ts @@ -0,0 +1,212 @@ +import * as vscode from "vscode"; +import type * as lc from "vscode-languageclient/node"; +import * as ra from "./lsp_ext"; + +import type { Ctx } from "./ctx"; +import { startDebugSession } from "./debug"; + +export const prepareTestExplorer = ( + ctx: Ctx, + testController: vscode.TestController, + client: lc.LanguageClient, +) => { + let currentTestRun: vscode.TestRun | undefined; + let idToTestMap: Map<string, vscode.TestItem> = new Map(); + const fileToTestMap: Map<string, vscode.TestItem[]> = new Map(); + const idToRunnableMap: Map<string, ra.Runnable> = new Map(); + + testController.createRunProfile( + "Run Tests", + vscode.TestRunProfileKind.Run, + async (request: vscode.TestRunRequest, cancelToken: vscode.CancellationToken) => { + if (currentTestRun) { + await client.sendNotification(ra.abortRunTest); + while (currentTestRun) { + await new Promise((resolve) => setTimeout(resolve, 1)); + } + } + + currentTestRun = testController.createTestRun(request); + cancelToken.onCancellationRequested(async () => { + await client.sendNotification(ra.abortRunTest); + }); + const include = request.include?.map((x) => x.id); + const exclude = request.exclude?.map((x) => x.id); + await client.sendRequest(ra.runTest, { include, exclude }); + }, + true, + undefined, + false, + ); + + testController.createRunProfile( + "Debug Tests", + vscode.TestRunProfileKind.Debug, + async (request: vscode.TestRunRequest) => { + if (request.include?.length !== 1 || request.exclude?.length !== 0) { + await vscode.window.showErrorMessage("You can debug only one test at a time"); + return; + } + const id = request.include[0]!.id; + const runnable = idToRunnableMap.get(id); + if (!runnable) { + await vscode.window.showErrorMessage("You can debug only one test at a time"); + return; + } + await startDebugSession(ctx, runnable); + }, + true, + undefined, + false, + ); + + const deleteTest = (item: vscode.TestItem, parentList: vscode.TestItemCollection) => { + parentList.delete(item.id); + idToTestMap.delete(item.id); + idToRunnableMap.delete(item.id); + if (item.uri) { + fileToTestMap.set( + item.uri.toString(), + fileToTestMap.get(item.uri.toString())!.filter((t) => t.id !== item.id), + ); + } + }; + + const addTest = (item: ra.TestItem) => { + const parentList = item.parent + ? idToTestMap.get(item.parent)!.children + : testController.items; + const oldTest = parentList.get(item.id); + const uri = item.textDocument?.uri ? vscode.Uri.parse(item.textDocument?.uri) : undefined; + const range = + item.range && + new vscode.Range( + new vscode.Position(item.range.start.line, item.range.start.character), + new vscode.Position(item.range.end.line, item.range.end.character), + ); + if (oldTest) { + if (oldTest.uri?.toString() === uri?.toString()) { + oldTest.range = range; + return; + } + deleteTest(oldTest, parentList); + } + const iconToVscodeMap = { + package: "package", + module: "symbol-module", + test: "beaker", + }; + const test = testController.createTestItem( + item.id, + `$(${iconToVscodeMap[item.kind]}) ${item.label}`, + uri, + ); + test.range = range; + test.canResolveChildren = item.canResolveChildren; + idToTestMap.set(item.id, test); + if (uri) { + if (!fileToTestMap.has(uri.toString())) { + fileToTestMap.set(uri.toString(), []); + } + fileToTestMap.get(uri.toString())!.push(test); + } + if (item.runnable) { + idToRunnableMap.set(item.id, item.runnable); + } + parentList.add(test); + }; + + const addTestGroup = (testsAndScope: ra.DiscoverTestResults) => { + const { tests, scope, scopeFile } = testsAndScope; + const testSet: Set<string> = new Set(); + for (const test of tests) { + addTest(test); + testSet.add(test.id); + } + // FIXME(hack_recover_crate_name): We eagerly resolve every test if we got a lazy top level response (detected + // by checking that `scope` is empty). This is not a good thing and wastes cpu and memory unnecessarily, so we + // should remove it. + if (!scope && !scopeFile) { + for (const test of tests) { + void testController.resolveHandler!(idToTestMap.get(test.id)); + } + } + if (scope) { + const recursivelyRemove = (tests: vscode.TestItemCollection) => { + for (const [_, test] of tests) { + if (!testSet.has(test.id)) { + deleteTest(test, tests); + } else { + recursivelyRemove(test.children); + } + } + }; + for (const root of scope) { + recursivelyRemove(idToTestMap.get(root)!.children); + } + } + if (scopeFile) { + const removeByFile = (file: vscode.Uri) => { + const testsToBeRemoved = (fileToTestMap.get(file.toString()) || []).filter( + (t) => !testSet.has(t.id), + ); + for (const test of testsToBeRemoved) { + const parentList = test.parent?.children || testController.items; + deleteTest(test, parentList); + } + }; + for (const file of scopeFile) { + removeByFile(vscode.Uri.parse(file.uri)); + } + } + }; + + ctx.pushClientCleanup( + client.onNotification(ra.discoveredTests, (results) => { + addTestGroup(results); + }), + ); + + ctx.pushClientCleanup( + client.onNotification(ra.endRunTest, () => { + currentTestRun!.end(); + currentTestRun = undefined; + }), + ); + + ctx.pushClientCleanup( + client.onNotification(ra.appendOutputToRunTest, (output) => { + currentTestRun!.appendOutput(`${output}\r\n`); + }), + ); + + ctx.pushClientCleanup( + client.onNotification(ra.changeTestState, (results) => { + const test = idToTestMap.get(results.testId)!; + if (results.state.tag === "failed") { + currentTestRun!.failed(test, new vscode.TestMessage(results.state.message)); + } else if (results.state.tag === "passed") { + currentTestRun!.passed(test); + } else if (results.state.tag === "started") { + currentTestRun!.started(test); + } else if (results.state.tag === "skipped") { + currentTestRun!.skipped(test); + } else if (results.state.tag === "enqueued") { + currentTestRun!.enqueued(test); + } + }), + ); + + testController.resolveHandler = async (item) => { + const results = await client.sendRequest(ra.discoverTest, { testId: item?.id }); + addTestGroup(results); + }; + + testController.refreshHandler = async () => { + testController.items.forEach((t) => { + testController.items.delete(t.id); + }); + idToTestMap = new Map(); + await testController.resolveHandler!(undefined); + }; +}; diff --git a/src/tools/rust-analyzer/editors/code/src/toolchain.ts b/src/tools/rust-analyzer/editors/code/src/toolchain.ts new file mode 100644 index 00000000000..a0b34406c1b --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/toolchain.ts @@ -0,0 +1,229 @@ +import * as cp from "child_process"; +import * as os from "os"; +import * as path from "path"; +import * as readline from "readline"; +import * as vscode from "vscode"; +import { execute, log, memoizeAsync } from "./util"; +import { unwrapNullable } from "./nullable"; +import { unwrapUndefinable } from "./undefinable"; + +interface CompilationArtifact { + fileName: string; + workspace: string; + name: string; + kind: string; + isTest: boolean; +} + +export interface ExecutableInfo { + executable: string; + workspace: string; +} + +export interface ArtifactSpec { + cargoArgs: string[]; + filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[]; +} + +export class Cargo { + constructor( + readonly rootFolder: string, + readonly output: vscode.OutputChannel, + readonly env: Record<string, string>, + ) {} + + // Made public for testing purposes + static artifactSpec(args: readonly string[]): ArtifactSpec { + const cargoArgs = [...args, "--message-format=json"]; + + // arguments for a runnable from the quick pick should be updated. + // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens + switch (cargoArgs[0]) { + case "run": + cargoArgs[0] = "build"; + break; + case "test": { + if (!cargoArgs.includes("--no-run")) { + cargoArgs.push("--no-run"); + } + break; + } + } + + const result: ArtifactSpec = { cargoArgs: cargoArgs }; + if (cargoArgs[0] === "test" || cargoArgs[0] === "bench") { + // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests + // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"} + result.filter = (artifacts) => artifacts.filter((it) => it.isTest); + } + + return result; + } + + private async getArtifacts(spec: ArtifactSpec): Promise<CompilationArtifact[]> { + const artifacts: CompilationArtifact[] = []; + + try { + await this.runCargo( + spec.cargoArgs, + (message) => { + if (message.reason === "compiler-artifact" && message.executable) { + const isBinary = message.target.crate_types.includes("bin"); + const isBuildScript = message.target.kind.includes("custom-build"); + if ((isBinary && !isBuildScript) || message.profile.test) { + artifacts.push({ + fileName: message.executable, + name: message.target.name, + workspace: path.dirname(message.manifest_path), + kind: message.target.kind[0], + isTest: message.profile.test, + }); + } + } else if (message.reason === "compiler-message") { + this.output.append(message.message.rendered); + } + }, + (stderr) => this.output.append(stderr), + ); + } catch (err) { + this.output.show(true); + throw new Error(`Cargo invocation has failed: ${err}`); + } + + return spec.filter?.(artifacts) ?? artifacts; + } + + async executableInfoFromArgs(args: readonly string[]): Promise<ExecutableInfo> { + const artifacts = await this.getArtifacts(Cargo.artifactSpec(args)); + + if (artifacts.length === 0) { + throw new Error("No compilation artifacts"); + } else if (artifacts.length > 1) { + throw new Error("Multiple compilation artifacts are not supported."); + } + + const artifact = unwrapUndefinable(artifacts[0]); + return { + executable: artifact.fileName, + workspace: artifact.workspace, + }; + } + + private async runCargo( + cargoArgs: string[], + onStdoutJson: (obj: any) => void, + onStderrString: (data: string) => void, + ): Promise<number> { + const path = await cargoPath(); + return await new Promise((resolve, reject) => { + const cargo = cp.spawn(path, cargoArgs, { + stdio: ["ignore", "pipe", "pipe"], + cwd: this.rootFolder, + env: this.env, + }); + + cargo.on("error", (err) => reject(new Error(`could not launch cargo: ${err}`))); + + cargo.stderr.on("data", (chunk) => onStderrString(chunk.toString())); + + const rl = readline.createInterface({ input: cargo.stdout }); + rl.on("line", (line) => { + const message = JSON.parse(line); + onStdoutJson(message); + }); + + cargo.on("exit", (exitCode, _) => { + if (exitCode === 0) resolve(exitCode); + else reject(new Error(`exit code: ${exitCode}.`)); + }); + }); + } +} + +/** Mirrors `project_model::sysroot::discover_sysroot_dir()` implementation*/ +export async function getSysroot(dir: string): Promise<string> { + const rustcPath = await getPathForExecutable("rustc"); + + // do not memoize the result because the toolchain may change between runs + return await execute(`${rustcPath} --print sysroot`, { cwd: dir }); +} + +export async function getRustcId(dir: string): Promise<string> { + const rustcPath = await getPathForExecutable("rustc"); + + // do not memoize the result because the toolchain may change between runs + const data = await execute(`${rustcPath} -V -v`, { cwd: dir }); + const rx = /commit-hash:\s(.*)$/m; + + const result = unwrapNullable(rx.exec(data)); + const first = unwrapUndefinable(result[1]); + return first; +} + +/** Mirrors `toolchain::cargo()` implementation */ +export function cargoPath(): Promise<string> { + return getPathForExecutable("cargo"); +} + +/** Mirrors `toolchain::get_path_for_executable()` implementation */ +export const getPathForExecutable = memoizeAsync( + // We apply caching to decrease file-system interactions + async (executableName: "cargo" | "rustc" | "rustup"): Promise<string> => { + { + const envVar = process.env[executableName.toUpperCase()]; + if (envVar) return envVar; + } + + if (await lookupInPath(executableName)) return executableName; + + const cargoHome = getCargoHome(); + if (cargoHome) { + const standardPath = vscode.Uri.joinPath(cargoHome, "bin", executableName); + if (await isFileAtUri(standardPath)) return standardPath.fsPath; + } + return executableName; + }, +); + +async function lookupInPath(exec: string): Promise<boolean> { + const paths = process.env["PATH"] ?? ""; + + const candidates = paths.split(path.delimiter).flatMap((dirInPath) => { + const candidate = path.join(dirInPath, exec); + return os.type() === "Windows_NT" ? [candidate, `${candidate}.exe`] : [candidate]; + }); + + for await (const isFile of candidates.map(isFileAtPath)) { + if (isFile) { + return true; + } + } + return false; +} + +function getCargoHome(): vscode.Uri | null { + const envVar = process.env["CARGO_HOME"]; + if (envVar) return vscode.Uri.file(envVar); + + try { + // hmm, `os.homedir()` seems to be infallible + // it is not mentioned in docs and cannot be inferred by the type signature... + return vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".cargo"); + } catch (err) { + log.error("Failed to read the fs info", err); + } + + return null; +} + +async function isFileAtPath(path: string): Promise<boolean> { + return isFileAtUri(vscode.Uri.file(path)); +} + +async function isFileAtUri(uri: vscode.Uri): Promise<boolean> { + try { + return ((await vscode.workspace.fs.stat(uri)).type & vscode.FileType.File) !== 0; + } catch { + return false; + } +} diff --git a/src/tools/rust-analyzer/editors/code/src/undefinable.ts b/src/tools/rust-analyzer/editors/code/src/undefinable.ts new file mode 100644 index 00000000000..813bac5a123 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/undefinable.ts @@ -0,0 +1,19 @@ +export type NotUndefined<T> = T extends undefined ? never : T; + +export type Undefinable<T> = T | undefined; + +function isNotUndefined<T>(input: Undefinable<T>): input is NotUndefined<T> { + return input !== undefined; +} + +export function expectNotUndefined<T>(input: Undefinable<T>, msg: string): NotUndefined<T> { + if (isNotUndefined(input)) { + return input; + } + + throw new TypeError(msg); +} + +export function unwrapUndefinable<T>(input: Undefinable<T>): NotUndefined<T> { + return expectNotUndefined(input, `unwrapping \`undefined\``); +} diff --git a/src/tools/rust-analyzer/editors/code/src/util.ts b/src/tools/rust-analyzer/editors/code/src/util.ts new file mode 100644 index 00000000000..51f921a2962 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/util.ts @@ -0,0 +1,201 @@ +import * as vscode from "vscode"; +import { strict as nativeAssert } from "assert"; +import { exec, type ExecOptions, spawnSync } from "child_process"; +import { inspect } from "util"; +import type { Env } from "./client"; + +export function assert(condition: boolean, explanation: string): asserts condition { + try { + nativeAssert(condition, explanation); + } catch (err) { + log.error(`Assertion failed:`, explanation); + throw err; + } +} + +export const log = new (class { + private enabled = true; + private readonly output = vscode.window.createOutputChannel("Rust Analyzer Client"); + + setEnabled(yes: boolean): void { + log.enabled = yes; + } + + // Hint: the type [T, ...T[]] means a non-empty array + debug(...msg: [unknown, ...unknown[]]): void { + if (!log.enabled) return; + log.write("DEBUG", ...msg); + } + + info(...msg: [unknown, ...unknown[]]): void { + log.write("INFO", ...msg); + } + + warn(...msg: [unknown, ...unknown[]]): void { + debugger; + log.write("WARN", ...msg); + } + + error(...msg: [unknown, ...unknown[]]): void { + debugger; + log.write("ERROR", ...msg); + log.output.show(true); + } + + private write(label: string, ...messageParts: unknown[]): void { + const message = messageParts.map(log.stringify).join(" "); + const dateTime = new Date().toLocaleString(); + log.output.appendLine(`${label} [${dateTime}]: ${message}`); + } + + private stringify(val: unknown): string { + if (typeof val === "string") return val; + return inspect(val, { + colors: false, + depth: 6, // heuristic + }); + } +})(); + +export function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +export type RustDocument = vscode.TextDocument & { languageId: "rust" }; +export type RustEditor = vscode.TextEditor & { document: RustDocument }; + +export function isRustDocument(document: vscode.TextDocument): document is RustDocument { + // Prevent corrupted text (particularly via inlay hints) in diff views + // by allowing only `file` schemes + // unfortunately extensions that use diff views not always set this + // to something different than 'file' (see ongoing bug: #4608) + return document.languageId === "rust" && document.uri.scheme === "file"; +} + +export function isCargoTomlDocument(document: vscode.TextDocument): document is RustDocument { + // ideally `document.languageId` should be 'toml' but user maybe not have toml extension installed + return document.uri.scheme === "file" && document.fileName.endsWith("Cargo.toml"); +} + +export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor { + return isRustDocument(editor.document); +} + +export function isDocumentInWorkspace(document: RustDocument): boolean { + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders) { + return false; + } + for (const folder of workspaceFolders) { + if (document.uri.fsPath.startsWith(folder.uri.fsPath)) { + return true; + } + } + return false; +} + +export function isValidExecutable(path: string, extraEnv: Env): boolean { + log.debug("Checking availability of a binary at", path); + + const res = spawnSync(path, ["--version"], { + encoding: "utf8", + env: { ...process.env, ...extraEnv }, + }); + + const printOutput = res.error ? log.warn : log.info; + printOutput(path, "--version:", res); + + return res.status === 0; +} + +/** Sets ['when'](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts) clause contexts */ +export function setContextValue(key: string, value: any): Thenable<void> { + return vscode.commands.executeCommand("setContext", key, value); +} + +/** + * Returns a higher-order function that caches the results of invoking the + * underlying async function. + */ +export function memoizeAsync<Ret, TThis, Param extends string>( + func: (this: TThis, arg: Param) => Promise<Ret>, +) { + const cache = new Map<string, Ret>(); + + return async function (this: TThis, arg: Param) { + const cached = cache.get(arg); + if (cached) return cached; + + const result = await func.call(this, arg); + cache.set(arg, result); + + return result; + }; +} + +/** Awaitable wrapper around `child_process.exec` */ +export function execute(command: string, options: ExecOptions): Promise<string> { + log.info(`running command: ${command}`); + return new Promise((resolve, reject) => { + exec(command, options, (err, stdout, stderr) => { + if (err) { + log.error(err); + reject(err); + return; + } + + if (stderr) { + reject(new Error(stderr)); + return; + } + + resolve(stdout.trimEnd()); + }); + }); +} + +export class LazyOutputChannel implements vscode.OutputChannel { + constructor(name: string) { + this.name = name; + } + + name: string; + _channel: vscode.OutputChannel | undefined; + + get channel(): vscode.OutputChannel { + if (!this._channel) { + this._channel = vscode.window.createOutputChannel(this.name); + } + return this._channel; + } + + append(value: string): void { + this.channel.append(value); + } + appendLine(value: string): void { + this.channel.appendLine(value); + } + replace(value: string): void { + this.channel.replace(value); + } + clear(): void { + if (this._channel) { + this._channel.clear(); + } + } + show(preserveFocus?: boolean): void; + show(column?: vscode.ViewColumn, preserveFocus?: boolean): void; + show(column?: any, preserveFocus?: any): void { + this.channel.show(column, preserveFocus); + } + hide(): void { + if (this._channel) { + this._channel.hide(); + } + } + dispose(): void { + if (this._channel) { + this._channel.dispose(); + } + } +} diff --git a/src/tools/rust-analyzer/editors/code/tests/runTests.ts b/src/tools/rust-analyzer/editors/code/tests/runTests.ts new file mode 100644 index 00000000000..08632ec3b49 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/tests/runTests.ts @@ -0,0 +1,43 @@ +import * as path from "path"; +import * as fs from "fs"; + +import { runTests } from "@vscode/test-electron"; + +async function main() { + // The folder containing the Extension Manifest package.json + // Passed to `--extensionDevelopmentPath` + const extensionDevelopmentPath = path.resolve(__dirname, "../../"); + + // Minimum supported version. + const jsonData = fs.readFileSync(path.join(extensionDevelopmentPath, "package.json")); + const json = JSON.parse(jsonData.toString()); + let minimalVersion: string = json.engines.vscode; + if (minimalVersion.startsWith("^")) minimalVersion = minimalVersion.slice(1); + + const launchArgs = ["--disable-extensions", extensionDevelopmentPath]; + + // All test suites (either unit tests or integration tests) should be in subfolders. + const extensionTestsPath = path.resolve(__dirname, "./unit/index"); + + // Run tests using the minimal supported version. + await runTests({ + version: minimalVersion, + launchArgs, + extensionDevelopmentPath, + extensionTestsPath, + }); + + // and the latest one + await runTests({ + version: "stable", + launchArgs, + extensionDevelopmentPath, + extensionTestsPath, + }); +} + +main().catch((err) => { + // eslint-disable-next-line no-console + console.error("Failed to run tests", err); + process.exit(1); +}); diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/index.ts b/src/tools/rust-analyzer/editors/code/tests/unit/index.ts new file mode 100644 index 00000000000..8ad46546abd --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/tests/unit/index.ts @@ -0,0 +1,87 @@ +import * as assert from "node:assert/strict"; +import { readdir } from "fs/promises"; +import * as path from "path"; + +class Test { + readonly name: string; + readonly promise: Promise<void>; + + constructor(name: string, promise: Promise<void>) { + this.name = name; + this.promise = promise; + } +} + +class Suite { + tests: Test[]; + + constructor() { + this.tests = []; + } + + public addTest(name: string, f: () => Promise<void>): void { + const test = new Test(name, f()); + this.tests.push(test); + } + + public async run(): Promise<void> { + let failed = 0; + for (const test of this.tests) { + try { + await test.promise; + ok(` ✔ ${test.name}`); + } catch (e) { + assert.ok(e instanceof Error); + error(` ✖︎ ${test.name}\n ${e.stack}`); + failed += 1; + } + } + if (failed) { + const plural = failed > 1 ? "s" : ""; + throw new Error(`${failed} failed test${plural}`); + } + } +} + +export class Context { + public async suite(name: string, f: (ctx: Suite) => void): Promise<void> { + const ctx = new Suite(); + f(ctx); + try { + ok(`⌛︎ ${name}`); + await ctx.run(); + ok(`✔ ${name}`); + } catch (e) { + assert.ok(e instanceof Error); + error(`✖︎ ${name}\n ${e.stack}`); + throw e; + } + } +} + +export async function run(): Promise<void> { + const context = new Context(); + + const testFiles = (await readdir(path.resolve(__dirname))).filter((name) => + name.endsWith(".test.js"), + ); + for (const testFile of testFiles) { + try { + const testModule = require(path.resolve(__dirname, testFile)); + await testModule.getTests(context); + } catch (e) { + error(`${e}`); + throw e; + } + } +} + +function ok(message: string): void { + // eslint-disable-next-line no-console + console.log(`\x1b[32m${message}\x1b[0m`); +} + +function error(message: string): void { + // eslint-disable-next-line no-console + console.error(`\x1b[31m${message}\x1b[0m`); +} diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/launch_config.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/launch_config.test.ts new file mode 100644 index 00000000000..eeb8608a42d --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/tests/unit/launch_config.test.ts @@ -0,0 +1,99 @@ +import * as assert from "assert"; +import { Cargo } from "../../src/toolchain"; +import type { Context } from "."; + +export async function getTests(ctx: Context) { + await ctx.suite("Launch configuration/Lens", (suite) => { + suite.addTest("A binary", async () => { + const args = Cargo.artifactSpec([ + "build", + "--package", + "pkg_name", + "--bin", + "pkg_name", + ]); + + assert.deepStrictEqual(args.cargoArgs, [ + "build", + "--package", + "pkg_name", + "--bin", + "pkg_name", + "--message-format=json", + ]); + assert.deepStrictEqual(args.filter, undefined); + }); + + suite.addTest("One of Multiple Binaries", async () => { + const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]); + + assert.deepStrictEqual(args.cargoArgs, [ + "build", + "--package", + "pkg_name", + "--bin", + "bin1", + "--message-format=json", + ]); + assert.deepStrictEqual(args.filter, undefined); + }); + + suite.addTest("A test", async () => { + const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]); + + assert.deepStrictEqual(args.cargoArgs, [ + "test", + "--package", + "pkg_name", + "--lib", + "--no-run", + "--message-format=json", + ]); + assert.notDeepStrictEqual(args.filter, undefined); + }); + }); + + await ctx.suite("Launch configuration/QuickPick", (suite) => { + suite.addTest("A binary", async () => { + const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]); + + assert.deepStrictEqual(args.cargoArgs, [ + "build", + "--package", + "pkg_name", + "--bin", + "pkg_name", + "--message-format=json", + ]); + assert.deepStrictEqual(args.filter, undefined); + }); + + suite.addTest("One of Multiple Binaries", async () => { + const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]); + + assert.deepStrictEqual(args.cargoArgs, [ + "build", + "--package", + "pkg_name", + "--bin", + "bin2", + "--message-format=json", + ]); + assert.deepStrictEqual(args.filter, undefined); + }); + + suite.addTest("A test", async () => { + const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]); + + assert.deepStrictEqual(args.cargoArgs, [ + "test", + "--package", + "pkg_name", + "--lib", + "--message-format=json", + "--no-run", + ]); + assert.notDeepStrictEqual(args.filter, undefined); + }); + }); +} diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/runnable_env.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/runnable_env.test.ts new file mode 100644 index 00000000000..b1407ce0193 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/tests/unit/runnable_env.test.ts @@ -0,0 +1,121 @@ +import * as assert from "assert"; +import { prepareEnv } from "../../src/run"; +import type { RunnableEnvCfg } from "../../src/config"; +import type { Context } from "."; +import type * as ra from "../../src/lsp_ext"; + +function makeRunnable(label: string): ra.Runnable { + return { + label, + kind: "cargo", + args: { + cargoArgs: [], + executableArgs: [], + cargoExtraArgs: [], + }, + }; +} + +function fakePrepareEnv(runnableName: string, config: RunnableEnvCfg): Record<string, string> { + const runnable = makeRunnable(runnableName); + return prepareEnv(runnable, config); +} + +export async function getTests(ctx: Context) { + await ctx.suite("Runnable env", (suite) => { + suite.addTest("Global config works", async () => { + const binEnv = fakePrepareEnv("run project_name", { GLOBAL: "g" }); + assert.strictEqual(binEnv["GLOBAL"], "g"); + + const testEnv = fakePrepareEnv("test some::mod::test_name", { GLOBAL: "g" }); + assert.strictEqual(testEnv["GLOBAL"], "g"); + }); + + suite.addTest("null mask works", async () => { + const config = [ + { + env: { DATA: "data" }, + }, + ]; + const binEnv = fakePrepareEnv("run project_name", config); + assert.strictEqual(binEnv["DATA"], "data"); + + const testEnv = fakePrepareEnv("test some::mod::test_name", config); + assert.strictEqual(testEnv["DATA"], "data"); + }); + + suite.addTest("order works", async () => { + const config = [ + { + env: { DATA: "data" }, + }, + { + env: { DATA: "newdata" }, + }, + ]; + const binEnv = fakePrepareEnv("run project_name", config); + assert.strictEqual(binEnv["DATA"], "newdata"); + + const testEnv = fakePrepareEnv("test some::mod::test_name", config); + assert.strictEqual(testEnv["DATA"], "newdata"); + }); + + suite.addTest("mask works", async () => { + const config = [ + { + env: { DATA: "data" }, + }, + { + mask: "^run", + env: { DATA: "rundata" }, + }, + { + mask: "special_test$", + env: { DATA: "special_test" }, + }, + ]; + const binEnv = fakePrepareEnv("run project_name", config); + assert.strictEqual(binEnv["DATA"], "rundata"); + + const testEnv = fakePrepareEnv("test some::mod::test_name", config); + assert.strictEqual(testEnv["DATA"], "data"); + + const specialTestEnv = fakePrepareEnv("test some::mod::special_test", config); + assert.strictEqual(specialTestEnv["DATA"], "special_test"); + }); + + suite.addTest("exact test name works", async () => { + const config = [ + { + env: { DATA: "data" }, + }, + { + mask: "some::mod::test_name", + env: { DATA: "test special" }, + }, + ]; + const testEnv = fakePrepareEnv("test some::mod::test_name", config); + assert.strictEqual(testEnv["DATA"], "test special"); + + const specialTestEnv = fakePrepareEnv("test some::mod::another_test", config); + assert.strictEqual(specialTestEnv["DATA"], "data"); + }); + + suite.addTest("test mod name works", async () => { + const config = [ + { + env: { DATA: "data" }, + }, + { + mask: "some::mod", + env: { DATA: "mod special" }, + }, + ]; + const testEnv = fakePrepareEnv("test some::mod::test_name", config); + assert.strictEqual(testEnv["DATA"], "mod special"); + + const specialTestEnv = fakePrepareEnv("test some::mod::another_test", config); + assert.strictEqual(specialTestEnv["DATA"], "mod special"); + }); + }); +} diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/settings.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/settings.test.ts new file mode 100644 index 00000000000..bafb9569a1c --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/tests/unit/settings.test.ts @@ -0,0 +1,63 @@ +import * as assert from "assert"; +import type { Context } from "."; +import { substituteVariablesInEnv } from "../../src/config"; + +export async function getTests(ctx: Context) { + await ctx.suite("Server Env Settings", (suite) => { + suite.addTest("Replacing Env Variables", async () => { + const envJson = { + USING_MY_VAR: "${env:MY_VAR} test ${env:MY_VAR}", + MY_VAR: "test", + }; + const expectedEnv = { + USING_MY_VAR: "test test test", + MY_VAR: "test", + }; + const actualEnv = await substituteVariablesInEnv(envJson); + assert.deepStrictEqual(actualEnv, expectedEnv); + }); + + suite.addTest("Circular dependencies remain as is", async () => { + const envJson = { + A_USES_B: "${env:B_USES_A}", + B_USES_A: "${env:A_USES_B}", + C_USES_ITSELF: "${env:C_USES_ITSELF}", + D_USES_C: "${env:C_USES_ITSELF}", + E_IS_ISOLATED: "test", + F_USES_E: "${env:E_IS_ISOLATED}", + }; + const expectedEnv = { + A_USES_B: "${env:B_USES_A}", + B_USES_A: "${env:A_USES_B}", + C_USES_ITSELF: "${env:C_USES_ITSELF}", + D_USES_C: "${env:C_USES_ITSELF}", + E_IS_ISOLATED: "test", + F_USES_E: "test", + }; + const actualEnv = await substituteVariablesInEnv(envJson); + assert.deepStrictEqual(actualEnv, expectedEnv); + }); + + suite.addTest("Should support external variables", async () => { + process.env["TEST_VARIABLE"] = "test"; + const envJson = { + USING_EXTERNAL_VAR: "${env:TEST_VARIABLE} test ${env:TEST_VARIABLE}", + }; + const expectedEnv = { + USING_EXTERNAL_VAR: "test test test", + }; + + const actualEnv = await substituteVariablesInEnv(envJson); + assert.deepStrictEqual(actualEnv, expectedEnv); + delete process.env["TEST_VARIABLE"]; + }); + + suite.addTest("should support VSCode variables", async () => { + const envJson = { + USING_VSCODE_VAR: "${workspaceFolderBasename}", + }; + const actualEnv = await substituteVariablesInEnv(envJson); + assert.deepStrictEqual(actualEnv["USING_VSCODE_VAR"], "code"); + }); + }); +} diff --git a/src/tools/rust-analyzer/editors/code/tsconfig.eslint.json b/src/tools/rust-analyzer/editors/code/tsconfig.eslint.json new file mode 100644 index 00000000000..5e2b33ca39f --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/tsconfig.eslint.json @@ -0,0 +1,11 @@ +// Special typescript project file, used by eslint only. +{ + "extends": "./tsconfig.json", + "include": [ + // repeated from base config's "include" setting + "src", + "tests", + // these are the eslint-only inclusions + ".eslintrc.js" + ] +} diff --git a/src/tools/rust-analyzer/editors/code/tsconfig.json b/src/tools/rust-analyzer/editors/code/tsconfig.json new file mode 100644 index 00000000000..87cfd1b2ee1 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "@tsconfig/strictest/tsconfig.json", + "compilerOptions": { + "esModuleInterop": false, + "module": "Node16", + "moduleResolution": "Node16", + "target": "ES2021", + "outDir": "out", + "lib": ["ES2021"], + "sourceMap": true, + "rootDir": ".", + "newLine": "lf", + + // FIXME: https://github.com/rust-lang/rust-analyzer/issues/15253 + "exactOptionalPropertyTypes": false + }, + "exclude": ["node_modules", ".vscode-test"], + "include": ["src", "tests"] +} |
