This commit is contained in:
Nick Trogh 2024-05-28 16:23:56 +02:00
Родитель 46511b8392
Коммит f9239b534c
2 изменённых файлов: 23 добавлений и 18 удалений

Просмотреть файл

@ -9,6 +9,8 @@ Author: Dirk Bäumer
# Using WebAssembly for Extension Development
April 30, 2024 by Dirk Bäumer
Visual Studio Code supports the execution of WASM binaries through the [WebAssembly Execution Engine](https://marketplace.visualstudio.com/items?itemName=ms-vscode.wasm-wasi-core) extension. The primary use case is to compile programs written in C/C++ or Rust into WebAssembly, and then run these programs directly in VS Code. A notable example is [Visual Studio Code for Education](https://vscodeedu.com/), which utilizes this support to run the Python interpreter in VS Code for the Web. This [blog post](https://code.visualstudio.com/blogs/2023/06/05/vscode-wasm-wasi) provides detailed insights into how this is implemented.
In January 2024, the Bytecode Alliance launched the [WASI 0.2 preview](https://bytecodealliance.org/articles/WASI-0.2). A key technology in the WASI 0.2 preview is the [Component Model](https://github.com/WebAssembly/component-model/). The WebAssembly Component Model streamlines interactions between WebAssembly components and their host environments by standardizing interfaces, data types, and module composition. This standardization is facilitated through the use of a WIT ([WASM Interface Type](https://component-model.bytecodealliance.org/design/wit.html)) file. WIT files help describe the interactions between a JavaScript/TypeScript extension (the host) and a WebAssembly component performing computations coded in another language, such as Rust or C/C++.

Просмотреть файл

@ -2,25 +2,26 @@
Order: 87
TOCTitle: VS Code Extensions and WebAssembly - Part Two
PageTitle: VS Code Extensions and WebAssembly - Part Two
MetaDescription: Using WebAssembly for Extension Development.
Date: 2024-04-30
MetaDescription: Use WebAssembly in VS Code extensions to run in a separate worker, or write a language server with a language that compiles to WebAssembly.
Date: 2024-05-28
Author: Dirk Bäumer
---
# Using WebAssembly for Extension Development - Part Two
Last month's blog post about using [WebAssembly for Extension Development]() demonstrated how the component model can be used to easily integrate WebAssembly code into your extension. This blog post will focus on two additional use cases: (a) how to run the WebAssembly code in a worker to avoid blocking the extension host's main thread, and (b) how to write a language server using a language that compiles to WebAssembly.
May 28, 2024 by Dirk Bäumer
The examples require that you have the latest versions of the following tools installed, in addition to VS Code and NodeJS: the [Rust compiler toolchain](https://www.rust-lang.org/), [wasm-tools](https://github.com/bytecodealliance/wasm-tools), and [wit-bindgen](https://github.com/bytecodealliance/wit-bindgen).
In the previous blog post about using [WebAssembly for Extension Development](/blogs/2024/05/08/wasm.md), we demonstrated how the component model can be used to integrate WebAssembly code in your Visual Studio Code extension. In this blog post, we focus on two additional use cases: (a) run the WebAssembly code in a worker to avoid blocking the extension host's main thread, and (b) create a language server by using a language that compiles to WebAssembly.
To run the examples in this blog post, you need the following tools: VS Code, Node.js, the [Rust compiler toolchain](https://www.rust-lang.org/), [wasm-tools](https://github.com/bytecodealliance/wasm-tools), and [wit-bindgen](https://github.com/bytecodealliance/wit-bindgen).
## Executing WebAssembly code in a Worker
## Executing WebAssembly code in a worker
All the examples provided in the previous blog post executed the WebAssembly code inside VS Code's extension host main thread. This is fine as long as the execution time is short. However, long-running operations should be executed in a worker to ensure that the extension host main thread remains available for other extensions.
The examples in the previous blog post ran the WebAssembly code in the VS Code extension host main thread. This is fine as long as the execution time is short. However, long-running operations should be executed in a worker to ensure that the extension host main thread remains available for other extensions.
Doing so is quite easy since VS Code's component model implementation provides a meta model that allows us to generate all the necessary glue code on both the worker and extension main side automatically.
The VS Code component model provides a meta model makes this easier by enabling us to automatically generate the necessary glue code on both the worker and extension main side.
Below is the code necessary for the worker. The example assumes that the code is stored in a file named `worker.ts`:
The following code snippet shows the necessary code for the worker. The example assumes that the code is stored in a file named `worker.ts`:
```typescript
import { Connection, RAL } from '@vscode/wasm-component-model';
@ -34,9 +35,9 @@ async function main(): Promise<void> {
main().catch(RAL().console.error);
```
All the code does is create a connection to communicate with the extension host main worker and initialize the connection with the `calculator` world generated by the `wit2ts` tool.
All the code does, is create a connection to communicate with the extension host main worker and initialize the connection with the `calculator` world that is generated by the `wit2ts` tool.
On the extension side, all we need to do is load the WebAssembly module and bind it to the world. The corresponding calls to perform the calculations need to be awaited since the execution happens asynchronously in the worker (e.g., `await api.calc(...)`).
On the extension side, all we need to do, is load the WebAssembly module and bind it to the world. The corresponding calls to perform the calculations need to be awaited since the execution happens asynchronously in the worker (for example, `await api.calc(...)`).
```typescript
// The channel for printing the result.
@ -77,11 +78,13 @@ vscode.commands.registerCommand('vscode-samples.wasm-component-model-async.run',
});
```
## A WebAssembly based Language Server
## A WebAssembly based language server
When we started working on [WebAssembly support for VS Code for the Web](https://code.visualstudio.com/blogs/2023/06/05/vscode-wasm-wasi), one of our envisioned use cases was to execute language servers using WebAssembly. With the latest changes to [VS Code's LSP libraries](https://github.com/Microsoft/vscode-languageserver-node) and the introduction of a new module to bridge between WebAssembly and LSP, implementing a WebAssembly language server is now as straightforward as implementing it as an operating system process. Additionally, WebAssembly language servers run on the [WebAssembly Core Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.wasm-wasi-core), which fully supports WASI Preview 1. This means that language servers can access the files in the workspace using the normal filesystem API of their programming language, even if the files are stored remotely (e.g., in a GitHub repository).
When we started working on [WebAssembly support for VS Code for the Web](https://code.visualstudio.com/blogs/2023/06/05/vscode-wasm-wasi), one of our envisioned use cases was to execute language servers using WebAssembly. With the latest changes to [VS Code's LSP libraries](https://github.com/Microsoft/vscode-languageserver-node) and the introduction of a new module to bridge between WebAssembly and LSP, implementing a WebAssembly language server is now as straightforward as implementing it as an operating system process.
Below is a code snippet of a Rust language server based on the [example server](https://insiders.vscode.dev/github.com/rust-lang/rust-analyzer/blob/master/lib/lsp-server/examples/goto_def.rs#L1) from the `lsp_server` crate. It doesn't perform any language analysis. It simply returns a predefined result for a `GotoDefinition` request:
Additionally, WebAssembly language servers run on the [WebAssembly Core Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.wasm-wasi-core), which fully supports WASI Preview 1. This means that language servers can access the files in the workspace using the regular filesystem API of their programming language, even if the files are stored remotely, such as in a GitHub repository.
Below is a code snippet of a Rust language server, based on the [example server](https://insiders.vscode.dev/github.com/rust-lang/rust-analyzer/blob/master/lib/lsp-server/examples/goto_def.rs#L1) from the `lsp_server` crate. This language server doesn't perform any language analysis but simply returns a predefined result for a `GotoDefinition` request:
```rust
match cast::<GotoDefinition>(req) {
@ -105,9 +108,9 @@ match cast::<GotoDefinition>(req) {
};
```
The full source code of the language server can be found in [VS Code's example repository](https://insiders.vscode.dev/github/microsoft/vscode-extension-samples/blob/main/wasm-language-server/server/src/main.rs#L1).
You can find the full source code of the language server in the [VS Code example repository](https://insiders.vscode.dev/github/microsoft/vscode-extension-samples/blob/main/wasm-language-server/server/src/main.rs#L1).
The new `@vscode/wasm-wasi-lsp` npm module can be used to easily create a WebAssembly language server within the extension's TypeScript code. Instantiating the WebAssembly code as a worker with WASI support is done using the [WebAssembly Core Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.wasm-wasi-core), which is described in detail in our [Run WebAssemblies in VS Code for the Web](https://code.visualstudio.com/blogs/2023/06/05/vscode-wasm-wasi) blog post.
You can use the new `@vscode/wasm-wasi-lsp` npm module to create a WebAssembly language server within the extension's TypeScript code. Instantiate the WebAssembly code as a worker with WASI support by using the [WebAssembly Core Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.wasm-wasi-core), which is described in detail in our [Run WebAssemblies in VS Code for the Web](https://code.visualstudio.com/blogs/2023/06/05/vscode-wasm-wasi) blog post.
The TypeScript code of the extension is straightforward as well. It registers the server for plain text files.
@ -155,13 +158,13 @@ export async function activate(context: ExtensionContext) {
}
```
Running the code will add a `Goto Definition` entry to the context menu of plain text files. Executing this action will send a corresponding request to the LSP server.
Running the code adds a `Goto Definition` entry to the context menu of plain text files. Executing this action sends a corresponding request to the LSP server.
![Running the goto definition action](goto-definition.png)
It is important to note that the `@vscode/wasm-wasi-lsp` npm module automatically transforms document URIs from their workspace value to the one recognized in the WASI Preview 1 host. In the above example, the text document's URI inside VS Code is usually something like `vscode-vfs://github/dbaeumer/plaintext-sample/lorem.txt`, and this value gets transformed into `file:///workspace/lorem.txt`, which is recognized inside the WASI host. This transformation also happens automatically when the language server sends a URI back to VS Code.
Most language server libraries support custom messages, making it easy to add features to a language server that are not already present in the [Language Server Protocol Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/). Below is a code snippet that adds a custom message handler to the Rust language server mentioned above, which counts the files in a given workspace folder:
Most language server libraries support custom messages, which makes it easy to add features to a language server that are not already present in the [Language Server Protocol Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/). The following code snippet shows how to add a custom message handler for counting the files in a given workspace folder to the Rust language server we used previously:
```rust
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
@ -223,7 +226,7 @@ It should also be highlighted that a language server doesn't necessarily need to
## What Comes Next
As already mentioned in the previous blog post we will continue our efforts to implement the WASI 0.2 preview for VS Code. We also plan to look into broadening the examples to other languages that compile to WASM.
As mentioned in the previous blog post, we continue our efforts to implement the WASI 0.2 preview for VS Code. We also plan to look into broadening the examples to other languages that compile to WASM.
Thanks,