about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/editors/code
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/editors/code')
-rw-r--r--src/tools/rust-analyzer/editors/code/.eslintignore2
-rw-r--r--src/tools/rust-analyzer/editors/code/.eslintrc.js46
-rw-r--r--src/tools/rust-analyzer/editors/code/.gitignore7
-rw-r--r--src/tools/rust-analyzer/editors/code/.prettierignore3
-rw-r--r--src/tools/rust-analyzer/editors/code/.prettierrc.js5
-rw-r--r--src/tools/rust-analyzer/editors/code/.vscodeignore14
-rw-r--r--src/tools/rust-analyzer/editors/code/LICENSE230
-rw-r--r--src/tools/rust-analyzer/editors/code/README.md52
-rw-r--r--src/tools/rust-analyzer/editors/code/icon.pngbin0 -> 15341 bytes
-rw-r--r--src/tools/rust-analyzer/editors/code/language-configuration.json46
-rw-r--r--src/tools/rust-analyzer/editors/code/package-lock.json4001
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json2295
-rw-r--r--src/tools/rust-analyzer/editors/code/ra_syntax_tree.tmGrammar.json29
-rw-r--r--src/tools/rust-analyzer/editors/code/src/ast_inspector.ts217
-rw-r--r--src/tools/rust-analyzer/editors/code/src/bootstrap.ts144
-rw-r--r--src/tools/rust-analyzer/editors/code/src/client.ts452
-rw-r--r--src/tools/rust-analyzer/editors/code/src/commands.ts1488
-rw-r--r--src/tools/rust-analyzer/editors/code/src/config.ts528
-rw-r--r--src/tools/rust-analyzer/editors/code/src/ctx.ts515
-rw-r--r--src/tools/rust-analyzer/editors/code/src/debug.ts266
-rw-r--r--src/tools/rust-analyzer/editors/code/src/dependencies_provider.ts154
-rw-r--r--src/tools/rust-analyzer/editors/code/src/diagnostics.ts220
-rw-r--r--src/tools/rust-analyzer/editors/code/src/lang_client.ts26
-rw-r--r--src/tools/rust-analyzer/editors/code/src/lsp_ext.ts271
-rw-r--r--src/tools/rust-analyzer/editors/code/src/main.ts216
-rw-r--r--src/tools/rust-analyzer/editors/code/src/nullable.ts19
-rw-r--r--src/tools/rust-analyzer/editors/code/src/persistent_state.ts20
-rw-r--r--src/tools/rust-analyzer/editors/code/src/run.ts225
-rw-r--r--src/tools/rust-analyzer/editors/code/src/rust_project.ts110
-rw-r--r--src/tools/rust-analyzer/editors/code/src/snippets.ts143
-rw-r--r--src/tools/rust-analyzer/editors/code/src/tasks.ts160
-rw-r--r--src/tools/rust-analyzer/editors/code/src/test_explorer.ts212
-rw-r--r--src/tools/rust-analyzer/editors/code/src/toolchain.ts229
-rw-r--r--src/tools/rust-analyzer/editors/code/src/undefinable.ts19
-rw-r--r--src/tools/rust-analyzer/editors/code/src/util.ts201
-rw-r--r--src/tools/rust-analyzer/editors/code/tests/runTests.ts43
-rw-r--r--src/tools/rust-analyzer/editors/code/tests/unit/index.ts87
-rw-r--r--src/tools/rust-analyzer/editors/code/tests/unit/launch_config.test.ts99
-rw-r--r--src/tools/rust-analyzer/editors/code/tests/unit/runnable_env.test.ts121
-rw-r--r--src/tools/rust-analyzer/editors/code/tests/unit/settings.test.ts63
-rw-r--r--src/tools/rust-analyzer/editors/code/tsconfig.eslint.json11
-rw-r--r--src/tools/rust-analyzer/editors/code/tsconfig.json19
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\
+ * &emsp;|-beches\
+ * &emsp;|-src\
+ * &emsp;|- ...
+ * - std
+ * 2. reveal every children of src:
+ * core
+ * alloc\
+ * &emsp;|-beches\
+ * &emsp;|-src\
+ * &emsp;&emsp;|- lib.rs\
+ * &emsp;&emsp;|- str.rs <------- FOUND IT!\
+ * &emsp;&emsp;|- ...\
+ * &emsp;|- ...\
+ * 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('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', ' & quot; ').replaceAll("'", '&#039;')
+    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"]
+}