Add wasm based examples (#1018)

* WIP

* Add custom add request

* Update package.json

* Component model resource example

* Update to latest wit-bindgen

* WIP

* Minor example updates

* Update wasm-lsp example

* Added some minor comments

* Minor renames

* Change lsp server to count files

* More model resource work

* WIP

* Make the example work

* Update examples to latest

* Update sample to latest tooling

* Update sample to latest wit2ts tooling

* WIP

* Carry over https://github.com/microsoft/vscode-docs/pull/7234/files

* Code cleanup

* Remove dist folder

* Remove another dist folder
This commit is contained in:
Dirk Bäumer
2024-05-07 16:12:01 +02:00
committed by GitHub
parent 12375b51fb
commit 209ce0e81b
35 changed files with 4743 additions and 8206 deletions

View File

@ -0,0 +1,5 @@
node_modules/**
out
dist
src/example.ts
bin

View File

@ -0,0 +1,21 @@
/**@type {import('eslint').Linter.Config} */
// eslint-disable-next-line no-undef
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
plugins: [
'@typescript-eslint',
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
],
rules: {
'semi': [2, "always"],
'@typescript-eslint/no-unused-vars': 0,
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/no-non-null-assertion': 0,
'@typescript-eslint/no-namespace': "off"
}
};

View File

@ -0,0 +1,2 @@
target
dist

View File

@ -0,0 +1,19 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "extensionHost",
"request": "launch",
"name": "Run Example",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceRoot}",
],
"outFiles": [
"${workspaceRoot}/out/**/*.js"
],
"sourceMaps": true,
"preLaunchTask": "npm: build"
}
]
}

313
wasm-component-model-resource/Cargo.lock generated Normal file
View File

@ -0,0 +1,313 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "calculator"
version = "0.1.0"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "id-arena"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "indexmap"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
"equivalent",
"hashbrown",
"serde",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "leb128"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "proc-macro2"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "semver"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
[[package]]
name = "serde"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.115"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "spdx"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ef1a0fa1e39ac22972c8db23ff89aea700ab96aa87114e1fb55937a631a0c9"
dependencies = [
"smallvec",
]
[[package]]
name = "syn"
version = "2.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "wasm-encoder"
version = "0.202.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfd106365a7f5f7aa3c1916a98cbb3ad477f5ff96ddb130285a91c6e7429e67a"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-metadata"
version = "0.202.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "094aea3cb90e09f16ee25a4c0e324b3e8c934e7fd838bfa039aef5352f44a917"
dependencies = [
"anyhow",
"indexmap",
"serde",
"serde_derive",
"serde_json",
"spdx",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.202.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6998515d3cf3f8b980ef7c11b29a9b1017d4cf86b99ae93b546992df9931413"
dependencies = [
"bitflags",
"indexmap",
"semver",
]
[[package]]
name = "wit-bindgen"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb4e7653763780be47e38f479e9aa83c768aa6a3b2ed086dc2826fdbbb7e7f5"
dependencies = [
"wit-bindgen-rt",
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b67e11c950041849a10828c7600ea62a4077c01e8af72e8593253575428f91b"
dependencies = [
"anyhow",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0780cf7046630ed70f689a098cd8d56c5c3b22f2a7379bbdb088879963ff96"
dependencies = [
"bitflags",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30acbe8fb708c3a830a33c4cb705df82659bf831b492ec6ca1a17a369cfeeafb"
dependencies = [
"anyhow",
"heck",
"indexmap",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b1b06eae85feaecdf9f2854f7cac124e00d5a6e5014bfb02eb1ecdeb5f265b9"
dependencies = [
"anyhow",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.202.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c836b1fd9932de0431c1758d8be08212071b6bba0151f7bac826dbc4312a2a9"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.202.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "744237b488352f4f27bca05a10acb79474415951c450e52ebd0da784c1df2bcc"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]

View File

@ -0,0 +1,12 @@
[package]
name = "calculator"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]
[dependencies]
wit-bindgen = "0.24.0"

View File

@ -0,0 +1,27 @@
# WASM Component Model Example
An example demonstrating how to use the component model to integrate WebAssembly code into VS Code.
## Functionality
A simple calculator that can perform add, sub, mul and div. The calculator is implemented in Rust and compiled to WebAssembly code and then call from JavaScript.
## Pre-requisites
To run the sample the following tool chains need to be installed
- [Rust](https://www.rust-lang.org/): installation instructions can be found [here](https://www.rust-lang.org/tools/install)
- [wasm-tools](https://github.com/bytecodealliance/wasm-tools): releases can be found [here](https://github.com/bytecodealliance/wasm-tools/releases). You need at least version >= 1.200 to run the example.
## Running the Sample in the Desktop
- Run `npm install` in this folder. This installs all necessary npm modules.
- Open VS Code on this folder.
- Execute the launch config `Run Example`.
## Running the Sample in the Web
As a pre-requisite follow the instructions [here](https://code.visualstudio.com/api/extension-guides/web-extensions#test-your-web-extension-in-vscode.dev) to generate necessary certificate to side load the extension into vscode.dev or insiders.vscode.dev.
Then compile the extension for the Web by running `npm run esbuild`, start a local extension server using `npm run serve`, open vscode.dev or insiders.vscode.dev in a browser and execute the command `Install Extension from Location`. As a location use `https://localhost:5000`.

View File

@ -0,0 +1,71 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
const esbuild = require('esbuild');
/**
* @typedef {import('esbuild').BuildOptions} BuildOptions
*/
/** @type BuildOptions */
const sharedWebOptions = {
bundle: true,
external: ['vscode'],
target: 'es2020',
platform: 'browser',
sourcemap: true,
};
/** @type BuildOptions */
const webOptions = {
entryPoints: ['src/extension.ts'],
outfile: 'dist/web/extension.js',
format: 'cjs',
...sharedWebOptions,
};
/** @type BuildOptions */
const sharedDesktopOptions = {
bundle: true,
external: ['vscode'],
target: 'es2020',
platform: 'node',
sourcemap: true,
};
/** @type BuildOptions */
const desktopOptions = {
entryPoints: ['src/extension.ts'],
outfile: 'dist/desktop/extension.js',
format: 'cjs',
...sharedDesktopOptions,
};
function createContexts() {
return Promise.all([
esbuild.context(webOptions),
esbuild.context(desktopOptions),
]);
}
createContexts().then(contexts => {
if (process.argv[2] === '--watch') {
const promises = [];
for (const context of contexts) {
promises.push(context.watch());
}
return Promise.all(promises).then(() => { return undefined; });
} else {
const promises = [];
for (const context of contexts) {
promises.push(context.rebuild());
}
Promise.all(promises).then(async () => {
for (const context of contexts) {
await context.dispose();
}
}).then(() => { return undefined; }).catch(console.error);
}
}).catch(console.error);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,55 @@
{
"name": "wasm-component-model-resource",
"description": "An example implementing a calculator using a resource.",
"author": "Microsoft Corporation",
"license": "MIT",
"version": "1.0.0",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/vscode-extension-samples"
},
"publisher": "vscode-samples",
"categories": [],
"keywords": [
"WASM",
"Component Model"
],
"engines": {
"vscode": "^1.88.0"
},
"main": "./out/extension",
"browser": "./dist/web/extension",
"contributes": {
"commands": [
{
"command": "vscode-samples.wasm-component-model.run",
"category": "Samples",
"title": "Run Calc Service"
}
]
},
"dependencies": {
"@vscode/wasm-component-model": "0.1.0-pre.11"
},
"devDependencies": {
"@types/vscode": "1.88.0",
"@types/node": "^18.14.6",
"@typescript-eslint/eslint-plugin": "^7.6.0",
"@typescript-eslint/parser": "^7.6.0",
"eslint": "^8.57.0",
"typescript": "^5.4.5",
"esbuild": "^0.20.2",
"serve": "^14.2.1"
},
"scripts": {
"vscode:prepublish": "npm run compile",
"build": "npm run compile && cargo build --target wasm32-unknown-unknown",
"wit-bindgen": "wit-bindgen rust --out-dir ./src ./wit",
"compile": "tsc -b",
"watch": "tsc -b -w",
"lint": "eslint ./src --ext .ts,.tsx",
"esbuild": "node ./bin/esbuild.js",
"generate:model": "wit2ts --outDir ./src ./wit",
"serve": "serve --cors -l 5000 --ssl-cert $HOME/certs/localhost.pem --ssl-key $HOME/certs/localhost-key.pem"
}
}

View File

@ -0,0 +1,545 @@
// Generated by `wit-bindgen` 0.24.0. DO NOT EDIT!
// Options used:
#[allow(dead_code)]
pub mod exports {
#[allow(dead_code)]
pub mod vscode {
#[allow(dead_code)]
pub mod example {
#[allow(dead_code, clippy::all)]
pub mod types {
#[used]
#[doc(hidden)]
#[cfg(target_arch = "wasm32")]
static __FORCE_SECTION_REF: fn() = super::super::super::super::__link_custom_section_describing_imports;
use super::super::super::super::_rt;
#[repr(u8)]
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Operation {
Add,
Sub,
Mul,
Div,
}
impl ::core::fmt::Debug for Operation {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
match self {
Operation::Add => {
f.debug_tuple("Operation::Add").finish()
}
Operation::Sub => {
f.debug_tuple("Operation::Sub").finish()
}
Operation::Mul => {
f.debug_tuple("Operation::Mul").finish()
}
Operation::Div => {
f.debug_tuple("Operation::Div").finish()
}
}
}
}
impl Operation{
pub(crate) unsafe fn _lift(val: u8) -> Operation{
if !cfg!(debug_assertions) {
return ::core::mem::transmute(val);
}
match val {
0 => Operation::Add,
1 => Operation::Sub,
2 => Operation::Mul,
3 => Operation::Div,
_ => panic!("invalid enum discriminant"),
}
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct Engine{
handle: _rt::Resource<Engine>,
}
type _EngineRep<T> = Option<T>;
impl Engine{
/// Creates a new resource from the specified representation.
///
/// This function will create a new resource handle by moving `val` onto
/// the heap and then passing that heap pointer to the component model to
/// create a handle. The owned handle is then returned as `Engine`.
pub fn new<T: GuestEngine>(val: T) -> Self {
Self::type_guard::<T>();
let val: _EngineRep<T> = Some(val);
let ptr: *mut _EngineRep<T> =
_rt::Box::into_raw(_rt::Box::new(val));
unsafe {
Self::from_handle(T::_resource_new(ptr.cast()))
}
}
/// Gets access to the underlying `T` which represents this resource.
pub fn get<T: GuestEngine>(&self) -> &T {
let ptr = unsafe { &*self.as_ptr::<T>() };
ptr.as_ref().unwrap()
}
/// Gets mutable access to the underlying `T` which represents this
/// resource.
pub fn get_mut<T: GuestEngine>(&mut self) -> &mut T {
let ptr = unsafe { &mut *self.as_ptr::<T>() };
ptr.as_mut().unwrap()
}
/// Consumes this resource and returns the underlying `T`.
pub fn into_inner<T: GuestEngine>(self) -> T {
let ptr = unsafe { &mut *self.as_ptr::<T>() };
ptr.take().unwrap()
}
#[doc(hidden)]
pub unsafe fn from_handle(handle: u32) -> Self {
Self {
handle: _rt::Resource::from_handle(handle),
}
}
#[doc(hidden)]
pub fn take_handle(&self) -> u32 {
_rt::Resource::take_handle(&self.handle)
}
#[doc(hidden)]
pub fn handle(&self) -> u32 {
_rt::Resource::handle(&self.handle)
}
// It's theoretically possible to implement the `GuestEngine` trait twice
// so guard against using it with two different types here.
#[doc(hidden)]
fn type_guard<T: 'static>() {
use core::any::TypeId;
static mut LAST_TYPE: Option<TypeId> = None;
unsafe {
assert!(!cfg!(target_feature = "threads"));
let id = TypeId::of::<T>();
match LAST_TYPE {
Some(ty) => assert!(ty == id, "cannot use two types with this resource type"),
None => LAST_TYPE = Some(id),
}
}
}
#[doc(hidden)]
pub unsafe fn dtor<T: 'static>(handle: *mut u8) {
Self::type_guard::<T>();
let _ = _rt::Box::from_raw(handle as *mut _EngineRep<T>);
}
fn as_ptr<T: GuestEngine>(&self) -> *mut _EngineRep<T> {
Engine::type_guard::<T>();
T::_resource_rep(self.handle()).cast()
}
}
/// A borrowed version of [`Engine`] which represents a borrowed value
/// with the lifetime `'a`.
#[derive(Debug)]
#[repr(transparent)]
pub struct EngineBorrow<'a> {
rep: *mut u8,
_marker: core::marker::PhantomData<&'a Engine>,
}
impl<'a> EngineBorrow<'a>{
#[doc(hidden)]
pub unsafe fn lift(rep: usize) -> Self {
Self {
rep: rep as *mut u8,
_marker: core::marker::PhantomData,
}
}
/// Gets access to the underlying `T` in this resource.
pub fn get<T: GuestEngine>(&self) -> &T {
let ptr = unsafe { &mut *self.as_ptr::<T>() };
ptr.as_ref().unwrap()
}
// NB: mutable access is not allowed due to the component model allowing
// multiple borrows of the same resource.
fn as_ptr<T: 'static>(&self) -> *mut _EngineRep<T> {
Engine::type_guard::<T>();
self.rep.cast()
}
}
unsafe impl _rt::WasmResource for Engine{
#[inline]
unsafe fn drop(_handle: u32) {
#[cfg(not(target_arch = "wasm32"))]
unreachable!();
#[cfg(target_arch = "wasm32")]
{
#[link(wasm_import_module = "[export]vscode:example/types")]
extern "C" {
#[link_name = "[resource-drop]engine"]
fn drop(_: u32);
}
drop(_handle);
}
}
}
#[doc(hidden)]
#[allow(non_snake_case)]
pub unsafe fn _export_constructor_engine_cabi<T: GuestEngine>() -> i32 {#[cfg(target_arch="wasm32")]
_rt::run_ctors_once();let result0 = Engine::new(T::new());
(result0).take_handle() as i32
}
#[doc(hidden)]
#[allow(non_snake_case)]
pub unsafe fn _export_method_engine_push_operand_cabi<T: GuestEngine>(arg0: *mut u8,arg1: i32,) {#[cfg(target_arch="wasm32")]
_rt::run_ctors_once();T::push_operand(EngineBorrow::lift(arg0 as u32 as usize).get(), arg1 as u32);
}
#[doc(hidden)]
#[allow(non_snake_case)]
pub unsafe fn _export_method_engine_push_operation_cabi<T: GuestEngine>(arg0: *mut u8,arg1: i32,) {#[cfg(target_arch="wasm32")]
_rt::run_ctors_once();T::push_operation(EngineBorrow::lift(arg0 as u32 as usize).get(), Operation::_lift(arg1 as u8));
}
#[doc(hidden)]
#[allow(non_snake_case)]
pub unsafe fn _export_method_engine_execute_cabi<T: GuestEngine>(arg0: *mut u8,) -> i32 {#[cfg(target_arch="wasm32")]
_rt::run_ctors_once();let result0 = T::execute(EngineBorrow::lift(arg0 as u32 as usize).get());
_rt::as_i32(result0)
}
pub trait Guest {
type Engine: GuestEngine;
}
pub trait GuestEngine: 'static {
#[doc(hidden)]
unsafe fn _resource_new(val: *mut u8) -> u32
where Self: Sized
{
#[cfg(not(target_arch = "wasm32"))]
{
let _ = val;
unreachable!();
}
#[cfg(target_arch = "wasm32")]
{
#[link(wasm_import_module = "[export]vscode:example/types")]
extern "C" {
#[link_name = "[resource-new]engine"]
fn new(_: *mut u8) -> u32;
}
new(val)
}
}
#[doc(hidden)]
fn _resource_rep(handle: u32) -> *mut u8
where Self: Sized
{
#[cfg(not(target_arch = "wasm32"))]
{
let _ = handle;
unreachable!();
}
#[cfg(target_arch = "wasm32")]
{
#[link(wasm_import_module = "[export]vscode:example/types")]
extern "C" {
#[link_name = "[resource-rep]engine"]
fn rep(_: u32) -> *mut u8;
}
unsafe {
rep(handle)
}
}
}
fn new() -> Self;
fn push_operand(&self,operand: u32,);
fn push_operation(&self,operation: Operation,);
fn execute(&self,) -> u32;
}
#[doc(hidden)]
macro_rules! __export_vscode_example_types_cabi{
($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = {
#[export_name = "vscode:example/types#[constructor]engine"]
unsafe extern "C" fn export_constructor_engine() -> i32 {
$($path_to_types)*::_export_constructor_engine_cabi::<<$ty as $($path_to_types)*::Guest>::Engine>()
}
#[export_name = "vscode:example/types#[method]engine.push-operand"]
unsafe extern "C" fn export_method_engine_push_operand(arg0: *mut u8,arg1: i32,) {
$($path_to_types)*::_export_method_engine_push_operand_cabi::<<$ty as $($path_to_types)*::Guest>::Engine>(arg0, arg1)
}
#[export_name = "vscode:example/types#[method]engine.push-operation"]
unsafe extern "C" fn export_method_engine_push_operation(arg0: *mut u8,arg1: i32,) {
$($path_to_types)*::_export_method_engine_push_operation_cabi::<<$ty as $($path_to_types)*::Guest>::Engine>(arg0, arg1)
}
#[export_name = "vscode:example/types#[method]engine.execute"]
unsafe extern "C" fn export_method_engine_execute(arg0: *mut u8,) -> i32 {
$($path_to_types)*::_export_method_engine_execute_cabi::<<$ty as $($path_to_types)*::Guest>::Engine>(arg0)
}
const _: () = {
#[doc(hidden)]
#[export_name = "vscode:example/types#[dtor]engine"]
#[allow(non_snake_case)]
unsafe extern "C" fn dtor(rep: *mut u8) {
$($path_to_types)*::Engine::dtor::<
<$ty as $($path_to_types)*::Guest>::Engine
>(rep)
}
};
};);
}
#[doc(hidden)]
pub(crate) use __export_vscode_example_types_cabi;
}
}
}
}
mod _rt {
use core::fmt;
use core::marker;
use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
/// A type which represents a component model resource, either imported or
/// exported into this component.
///
/// This is a low-level wrapper which handles the lifetime of the resource
/// (namely this has a destructor). The `T` provided defines the component model
/// intrinsics that this wrapper uses.
///
/// One of the chief purposes of this type is to provide `Deref` implementations
/// to access the underlying data when it is owned.
///
/// This type is primarily used in generated code for exported and imported
/// resources.
#[repr(transparent)]
pub struct Resource<T: WasmResource> {
// NB: This would ideally be `u32` but it is not. The fact that this has
// interior mutability is not exposed in the API of this type except for the
// `take_handle` method which is supposed to in theory be private.
//
// This represents, almost all the time, a valid handle value. When it's
// invalid it's stored as `u32::MAX`.
handle: AtomicU32,
_marker: marker::PhantomData<T>,
}
/// A trait which all wasm resources implement, namely providing the ability to
/// drop a resource.
///
/// This generally is implemented by generated code, not user-facing code.
pub unsafe trait WasmResource {
/// Invokes the `[resource-drop]...` intrinsic.
unsafe fn drop(handle: u32);
}
impl<T: WasmResource> Resource<T> {
#[doc(hidden)]
pub unsafe fn from_handle(handle: u32) -> Self {
debug_assert!(handle != u32::MAX);
Self {
handle: AtomicU32::new(handle),
_marker: marker::PhantomData,
}
}
/// Takes ownership of the handle owned by `resource`.
///
/// Note that this ideally would be `into_handle` taking `Resource<T>` by
/// ownership. The code generator does not enable that in all situations,
/// unfortunately, so this is provided instead.
///
/// Also note that `take_handle` is in theory only ever called on values
/// owned by a generated function. For example a generated function might
/// take `Resource<T>` as an argument but then call `take_handle` on a
/// reference to that argument. In that sense the dynamic nature of
/// `take_handle` should only be exposed internally to generated code, not
/// to user code.
#[doc(hidden)]
pub fn take_handle(resource: &Resource<T>) -> u32 {
resource.handle.swap(u32::MAX, Relaxed)
}
#[doc(hidden)]
pub fn handle(resource: &Resource<T>) -> u32 {
resource.handle.load(Relaxed)
}
}
impl<T: WasmResource> fmt::Debug for Resource<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Resource")
.field("handle", &self.handle)
.finish()
}
}
impl<T: WasmResource> Drop for Resource<T> {
fn drop(&mut self) {
unsafe {
match self.handle.load(Relaxed) {
// If this handle was "taken" then don't do anything in the
// destructor.
u32::MAX => {}
// ... but otherwise do actually destroy it with the imported
// component model intrinsic as defined through `T`.
other => T::drop(other),
}
}
}
}
pub use alloc_crate::boxed::Box;
#[cfg(target_arch = "wasm32")]
pub fn run_ctors_once() {
wit_bindgen::rt::run_ctors_once();
}
pub fn as_i32<T: AsI32>(t: T) -> i32 {
t.as_i32()
}
pub trait AsI32 {
fn as_i32(self) -> i32;
}
impl<'a, T: Copy + AsI32> AsI32 for &'a T {
fn as_i32(self) -> i32 {
(*self).as_i32()
}
}
impl AsI32 for i32 {
#[inline]
fn as_i32(self) -> i32 {
self as i32
}
}
impl AsI32 for u32 {
#[inline]
fn as_i32(self) -> i32 {
self as i32
}
}
impl AsI32 for i16 {
#[inline]
fn as_i32(self) -> i32 {
self as i32
}
}
impl AsI32 for u16 {
#[inline]
fn as_i32(self) -> i32 {
self as i32
}
}
impl AsI32 for i8 {
#[inline]
fn as_i32(self) -> i32 {
self as i32
}
}
impl AsI32 for u8 {
#[inline]
fn as_i32(self) -> i32 {
self as i32
}
}
impl AsI32 for char {
#[inline]
fn as_i32(self) -> i32 {
self as i32
}
}
impl AsI32 for usize {
#[inline]
fn as_i32(self) -> i32 {
self as i32
}
}
extern crate alloc as alloc_crate;
}
/// Generates `#[no_mangle]` functions to export the specified type as the
/// root implementation of all generated traits.
///
/// For more information see the documentation of `wit_bindgen::generate!`.
///
/// ```rust
/// # macro_rules! export{ ($($t:tt)*) => (); }
/// # trait Guest {}
/// struct MyType;
///
/// impl Guest for MyType {
/// // ...
/// }
///
/// export!(MyType);
/// ```
#[allow(unused_macros)]
#[doc(hidden)]
macro_rules! __export_calculator_impl {
($ty:ident) => (self::export!($ty with_types_in self););
($ty:ident with_types_in $($path_to_types_root:tt)*) => (
$($path_to_types_root)*::exports::vscode::example::types::__export_vscode_example_types_cabi!($ty with_types_in $($path_to_types_root)*::exports::vscode::example::types);
)
}
#[doc(inline)]
pub(crate) use __export_calculator_impl as export;
#[cfg(target_arch = "wasm32")]
#[link_section = "component-type:wit-bindgen:0.24.0:calculator:encoded world"]
#[doc(hidden)]
pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 419] = *b"\
\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xa2\x02\x01A\x02\x01\
A\x02\x01B\x0d\x01m\x04\x03add\x03sub\x03mul\x03div\x04\0\x09operation\x03\0\0\x04\
\0\x06engine\x03\x01\x01i\x02\x01@\0\0\x03\x04\0\x13[constructor]engine\x01\x04\x01\
h\x02\x01@\x02\x04self\x05\x07operandy\x01\0\x04\0\x1b[method]engine.push-operan\
d\x01\x06\x01@\x02\x04self\x05\x09operation\x01\x01\0\x04\0\x1d[method]engine.pu\
sh-operation\x01\x07\x01@\x01\x04self\x05\0y\x04\0\x16[method]engine.execute\x01\
\x08\x04\x01\x14vscode:example/types\x05\0\x04\x01\x19vscode:example/calculator\x04\
\0\x0b\x10\x01\0\x0acalculator\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0d\
wit-component\x070.202.0\x10wit-bindgen-rust\x060.24.0";
#[inline(never)]
#[doc(hidden)]
#[cfg(target_arch = "wasm32")]
pub fn __link_custom_section_describing_imports() {
wit_bindgen::rt::maybe_link_cabi_realloc();
}

View File

@ -0,0 +1,178 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/* eslint-disable @typescript-eslint/ban-types */
import * as $wcm from '@vscode/wasm-component-model';
import type { u32, own, i32 } from '@vscode/wasm-component-model';
export namespace Types {
export enum Operation {
add = 'add',
sub = 'sub',
mul = 'mul',
div = 'div'
}
export namespace Engine {
export interface Interface extends $wcm.Resource {
pushOperand(operand: u32): void;
pushOperation(operation: Operation): void;
execute(): u32;
}
export type Statics = {
};
export type Class = Statics & {
new(): Interface;
};
}
export type Engine = Engine.Interface;
}
export type Types = {
Engine: Types.Engine.Class;
};
export namespace calculator {
export type Imports = {
};
export type Exports = {
types: Types;
};
}
export namespace Types.$ {
export const Operation = new $wcm.EnumType<Types.Operation>(['add', 'sub', 'mul', 'div']);
export const Engine = new $wcm.ResourceType<Types.Engine>('engine', 'vscode:example/types/engine');
export const Engine_Handle = new $wcm.ResourceHandleType('engine');
Engine.addDestructor('$drop', new $wcm.DestructorType('[resource-drop]engine', [['inst', Engine]]));
Engine.addConstructor('constructor', new $wcm.ConstructorType<Types.Engine.Class['constructor']>('[constructor]engine', [], new $wcm.OwnType(Engine_Handle)));
Engine.addMethod('pushOperand', new $wcm.MethodType<Types.Engine.Interface['pushOperand']>('[method]engine.push-operand', [
['operand', $wcm.u32],
], undefined));
Engine.addMethod('pushOperation', new $wcm.MethodType<Types.Engine.Interface['pushOperation']>('[method]engine.push-operation', [
['operation', Operation],
], undefined));
Engine.addMethod('execute', new $wcm.MethodType<Types.Engine.Interface['execute']>('[method]engine.execute', [], $wcm.u32));
}
export namespace Types._ {
export const id = 'vscode:example/types' as const;
export const witName = 'types' as const;
export namespace Engine {
export type WasmInterface = {
'[constructor]engine': () => i32;
'[method]engine.push-operand': (self: i32, operand: i32) => void;
'[method]engine.push-operation': (self: i32, operation_Operation: i32) => void;
'[method]engine.execute': (self: i32) => i32;
};
type ObjectModule = {
'constructor'(): own<$wcm.ResourceHandle>;
pushOperand(self: Engine, operand: u32): void;
pushOperation(self: Engine, operation: Operation): void;
execute(self: Engine): u32;
};
export namespace imports {
export type WasmInterface = Engine.WasmInterface & { '[resource-drop]engine': (self: i32) => void };
}
export namespace exports {
export type WasmInterface = Engine.WasmInterface & { '[dtor]engine': (self: i32) => void };
class Impl extends $wcm.Resource.Default implements Types.Engine.Interface {
private readonly _rep: $wcm.ResourceRepresentation;
private readonly _om: ObjectModule;
constructor(om: ObjectModule);
constructor(handleTag: symbol, handle: $wcm.ResourceHandle, rm: $wcm.ResourceManager, om: ObjectModule);
constructor(...args: any[]);
constructor(...args: any[]) {
if (args[0] === $wcm.ResourceManager.handleTag) {
const handle = args[1] as $wcm.ResourceHandle;
super(handle);
this._rep = (args[2] as $wcm.ResourceManager).getRepresentation(handle);
this._om = args[3] as ObjectModule;
} else {
const rm = args[0] as $wcm.ResourceManager;
const om = args[1] as ObjectModule;
super(om.constructor());
this._rep = rm.getRepresentation(this.$handle());
this._om = om;
}
}
public $rep(): $wcm.ResourceRepresentation { return this._rep; }
public pushOperand(operand: u32): void {
return this._om.pushOperand(this, operand);
}
public pushOperation(operation: Operation): void {
return this._om.pushOperation(this, operation);
}
public execute(): u32 {
return this._om.execute(this);
}
}
export function Class(wasmInterface: WasmInterface, context: $wcm.WasmContext): Types.Engine.Class {
const resource = Types.$.Engine;
const rm: $wcm.ResourceManager = context.resources.ensure('vscode:example/types/engine');
const om: ObjectModule = $wcm.Module.createObjectModule(resource, wasmInterface, context);
return class extends Impl {
constructor();
constructor(handleTag: symbol, handle: $wcm.ResourceHandle);
constructor(...args: any[]) {
super(...args, rm, om);
rm.registerProxy(this);
}
};
}
}
}
export const types: Map<string, $wcm.GenericComponentModelType> = new Map<string, $wcm.GenericComponentModelType>([
['Operation', $.Operation],
['Engine', $.Engine]
]);
export const resources: Map<string, { resource: $wcm.ResourceType; factory: $wcm.ClassFactory<any>}> = new Map<string, { resource: $wcm.ResourceType; factory: $wcm.ClassFactory<any>}>([
['Engine', { resource: $.Engine, factory: Engine.exports.Class }]
]);
export type WasmInterface = {
};
export namespace imports {
export type WasmInterface = _.WasmInterface & Engine.imports.WasmInterface;
}
export namespace exports {
export type WasmInterface = _.WasmInterface & Engine.exports.WasmInterface;
export namespace imports {
export type WasmInterface = {
'[resource-new]engine': (rep: i32) => i32;
'[resource-rep]engine': (handle: i32) => i32;
'[resource-drop]engine': (handle: i32) => void;
};
}
}
}
export namespace calculator.$ {
}
export namespace calculator._ {
export const id = 'vscode:example/calculator' as const;
export const witName = 'calculator' as const;
export type Imports = {
'[export]vscode:example/types': Types._.exports.imports.WasmInterface;
};
export namespace imports {
export function create(service: calculator.Imports, context: $wcm.WasmContext): Imports {
return $wcm.Imports.create<Imports>(_, service, context);
}
export function loop(service: calculator.Imports, context: $wcm.WasmContext): calculator.Imports {
return $wcm.Imports.loop(_, service, context);
}
}
export type Exports = {
'vscode:example/types#[constructor]engine': () => i32;
'vscode:example/types#[method]engine.push-operand': (self: i32, operand: i32) => void;
'vscode:example/types#[method]engine.push-operation': (self: i32, operation_Operation: i32) => void;
'vscode:example/types#[method]engine.execute': (self: i32) => i32;
};
export namespace exports {
export const interfaces: Map<string, $wcm.InterfaceType> = new Map<string, $wcm.InterfaceType>([
['Types', Types._]
]);
export function bind(exports: Exports, context: $wcm.WasmContext): calculator.Exports {
return $wcm.Exports.bind<calculator.Exports>(_, exports, context);
}
}
}

View File

@ -0,0 +1,53 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { WasmContext, Memory } from '@vscode/wasm-component-model';
import { calculator, Types } from './calculator';
export async function activate(context: vscode.ExtensionContext): Promise<void> {
// The channel for printing the result.
const channel = vscode.window.createOutputChannel('Calculator');
context.subscriptions.push(channel);
// The channel for printing the log.
const log = vscode.window.createOutputChannel('Calculator - Log', { log: true });
context.subscriptions.push(log);
// Load the Wasm module
const filename = vscode.Uri.joinPath(context.extensionUri, 'target', 'wasm32-unknown-unknown', 'debug', 'calculator.wasm');
const bits = await vscode.workspace.fs.readFile(filename);
const module = await WebAssembly.compile(bits);
// The context for the WASM module
const wasmContext: WasmContext.Default = new WasmContext.Default();
// Instantiate the module and create the necessary imports from the service implementation
const instance = await WebAssembly.instantiate(module, calculator._.imports.create({}, wasmContext));
// Bind the WASM memory to the context
wasmContext.initialize(new Memory.Default(instance.exports));
// Bind the JavaScript Api
const api = calculator._.exports.bind(instance.exports as calculator._.Exports, wasmContext);
context.subscriptions.push(vscode.commands.registerCommand('vscode-samples.wasm-component-model.run', () => {
channel.show();
channel.appendLine('Running calculator example');
// Create a new calculator engine
const calculator = new api.types.Engine();
// Push some operands and operations
calculator.pushOperand(10);
calculator.pushOperand(20);
calculator.pushOperation(Types.Operation.add);
calculator.pushOperand(2);
calculator.pushOperation(Types.Operation.mul);
// Calculate the result
const result = calculator.execute();
channel.appendLine(`Result: ${result}`);
}));
}

View File

@ -0,0 +1,77 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
mod calculator;
use std::cell::RefCell;
use crate::calculator::exports::vscode::example::types::{ Guest, GuestEngine, Operation };
struct EngineImpl {
left: Option<u32>,
right: Option<u32>,
}
impl EngineImpl {
fn new() -> Self {
EngineImpl {
left: None,
right: None,
}
}
fn push_operand(&mut self, operand: u32) {
if self.left == None {
self.left = Some(operand);
} else {
self.right = Some(operand);
}
}
fn push_operation(&mut self, operation: Operation) {
let left = self.left.unwrap();
let right = self.right.unwrap();
self.left = Some(match operation {
Operation::Add => left + right,
Operation::Sub => left - right,
Operation::Mul => left * right,
Operation::Div => left / right,
});
}
fn execute(&mut self) -> u32 {
self.left.unwrap()
}
}
struct CalcEngine {
stack: RefCell<EngineImpl>,
}
impl GuestEngine for CalcEngine {
fn new() -> Self {
CalcEngine {
stack: RefCell::new(EngineImpl::new())
}
}
fn push_operand(&self, operand: u32) {
self.stack.borrow_mut().push_operand(operand);
}
fn push_operation(&self,operation:Operation) {
self.stack.borrow_mut().push_operation(operation);
}
fn execute(&self) -> u32 {
return self.stack.borrow_mut().execute();
}
}
struct Implementation;
impl Guest for Implementation {
type Engine = CalcEngine;
}
calculator::export!(Implementation with_types_in calculator);

View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"rootDir": "./src",
"skipLibCheck": true,
"lib": [
"es2022",
"webworker"
],
"types": [
"vscode"
],
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./out",
"strict": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"declaration": true,
"stripInternal": true,
"sourceMap": true,
"declarationMap": true,
"noUnusedLocals": false,
"noUnusedParameters": false
}
}

View File

@ -0,0 +1,22 @@
// wit/calculator.wit
package vscode:example;
interface types {
enum operation {
add,
sub,
mul,
div
}
resource engine {
constructor();
push-operand: func(operand: u32);
push-operation: func(operation: operation);
execute: func() -> u32;
}
}
world calculator {
export types;
}

View File

@ -1 +1,2 @@
target
target
dist

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"@vscode/wasm-component-model": "0.1.0-pre.3"
"@vscode/wasm-component-model": "0.1.0-pre.11"
},
"devDependencies": {
"@types/node": "^18.14.6",
@ -794,9 +794,9 @@
"dev": true
},
"node_modules/@vscode/wasm-component-model": {
"version": "0.1.0-pre.3",
"resolved": "https://registry.npmjs.org/@vscode/wasm-component-model/-/wasm-component-model-0.1.0-pre.3.tgz",
"integrity": "sha512-1QgbcblhfBD3crZswJ3JchlwXvVSyYrxNMvVRxEkhjmW9LXYiOXFLT+iyc7yk95LSgYVWDrE5g4CRD2dXmB3sg==",
"version": "0.1.0-pre.11",
"resolved": "https://registry.npmjs.org/@vscode/wasm-component-model/-/wasm-component-model-0.1.0-pre.11.tgz",
"integrity": "sha512-52MPesjJMOgpv4K/Knek5J/szqquVx7Ji2IVfgEXYDUoE80u4aQSkPwnCBSoaIf4fECDTBtjijl4s7zAKq/58w==",
"dependencies": {
"semver": "^7.6.0",
"uuid": "^9.0.1",

View File

@ -29,7 +29,7 @@
]
},
"dependencies": {
"@vscode/wasm-component-model": "0.1.0-pre.3"
"@vscode/wasm-component-model": "0.1.0-pre.11"
},
"devDependencies": {
"@types/vscode": "1.88.0",
@ -48,7 +48,7 @@
"watch": "tsc -b -w",
"lint": "eslint ./src --ext .ts,.tsx",
"esbuild": "node ./bin/esbuild.js",
"generate:model": "wit2ts --noMain --outDir ./src ./wit",
"generate:model": "wit2ts --outDir ./src ./wit",
"serve": "serve --cors -l 5000 --ssl-cert $HOME/certs/localhost.pem --ssl-key $HOME/certs/localhost-key.pem"
}
}

View File

@ -0,0 +1,151 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/* eslint-disable @typescript-eslint/ban-types */
import * as $wcm from '@vscode/wasm-component-model';
import type { u32, i32 } from '@vscode/wasm-component-model';
export namespace Types {
export type Operands = {
left: u32;
right: u32;
};
export namespace Operation {
export const add = 'add' as const;
export type Add = { readonly tag: typeof add; readonly value: Operands } & _common;
export function Add(value: Operands): Add {
return new VariantImpl(add, value) as Add;
}
export const sub = 'sub' as const;
export type Sub = { readonly tag: typeof sub; readonly value: Operands } & _common;
export function Sub(value: Operands): Sub {
return new VariantImpl(sub, value) as Sub;
}
export const mul = 'mul' as const;
export type Mul = { readonly tag: typeof mul; readonly value: Operands } & _common;
export function Mul(value: Operands): Mul {
return new VariantImpl(mul, value) as Mul;
}
export const div = 'div' as const;
export type Div = { readonly tag: typeof div; readonly value: Operands } & _common;
export function Div(value: Operands): Div {
return new VariantImpl(div, value) as Div;
}
export type _tt = typeof add | typeof sub | typeof mul | typeof div;
export type _vt = Operands | Operands | Operands | Operands;
type _common = Omit<VariantImpl, 'tag' | 'value'>;
export function _ctor(t: _tt, v: _vt): Operation {
return new VariantImpl(t, v) as Operation;
}
class VariantImpl {
private readonly _tag: _tt;
private readonly _value: _vt;
constructor(t: _tt, value: _vt) {
this._tag = t;
this._value = value;
}
get tag(): _tt {
return this._tag;
}
get value(): _vt {
return this._value;
}
isAdd(): this is Add {
return this._tag === Operation.add;
}
isSub(): this is Sub {
return this._tag === Operation.sub;
}
isMul(): this is Mul {
return this._tag === Operation.mul;
}
isDiv(): this is Div {
return this._tag === Operation.div;
}
}
}
export type Operation = Operation.Add | Operation.Sub | Operation.Mul | Operation.Div;
}
export type Types = {
};
export namespace calculator {
export type Operation = Types.Operation;
export type Imports = {
log: (msg: string) => void;
};
export type Exports = {
calc: (o: Operation) => u32;
};
}
export namespace Types.$ {
export const Operands = new $wcm.RecordType<Types.Operands>([
['left', $wcm.u32],
['right', $wcm.u32],
]);
export const Operation = new $wcm.VariantType<Types.Operation, Types.Operation._tt, Types.Operation._vt>([['add', Operands], ['sub', Operands], ['mul', Operands], ['div', Operands]], Types.Operation._ctor);
}
export namespace Types._ {
export const id = 'vscode:example/types' as const;
export const witName = 'types' as const;
export const types: Map<string, $wcm.GenericComponentModelType> = new Map<string, $wcm.GenericComponentModelType>([
['Operands', $.Operands],
['Operation', $.Operation]
]);
export type WasmInterface = {
};
}
export namespace calculator.$ {
export const Operation = Types.$.Operation;
export namespace imports {
export const log = new $wcm.FunctionType<calculator.Imports['log']>('log',[
['msg', $wcm.wstring],
], undefined);
}
export namespace exports {
export const calc = new $wcm.FunctionType<calculator.Exports['calc']>('calc',[
['o', Operation],
], $wcm.u32);
}
}
export namespace calculator._ {
export const id = 'vscode:example/calculator' as const;
export const witName = 'calculator' as const;
export type $Root = {
'log': (msg_ptr: i32, msg_len: i32) => void;
};
export type Imports = {
'$root': $Root;
};
export namespace imports {
export const functions: Map<string, $wcm.FunctionType> = new Map([
['log', $.imports.log]
]);
export const interfaces: Map<string, $wcm.InterfaceType> = new Map<string, $wcm.InterfaceType>([
['Types', Types._]
]);
export function create(service: calculator.Imports, context: $wcm.WasmContext): Imports {
return $wcm.Imports.create<Imports>(_, service, context);
}
export function loop(service: calculator.Imports, context: $wcm.WasmContext): calculator.Imports {
return $wcm.Imports.loop(_, service, context);
}
}
export type Exports = {
'calc': (o_Operation_case: i32, o_Operation_0: i32, o_Operation_1: i32) => i32;
};
export namespace exports {
export const functions: Map<string, $wcm.FunctionType> = new Map([
['calc', $.exports.calc]
]);
export function bind(exports: Exports, context: $wcm.WasmContext): calculator.Exports {
return $wcm.Exports.bind<calculator.Exports>(_, exports, context);
}
}
}

View File

@ -1,164 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as $wcm from '@vscode/wasm-component-model';
import type { u32, i32 } from '@vscode/wasm-component-model';
export namespace example {
export namespace Types {
export type Operands = {
left: u32;
right: u32;
};
export namespace Operation {
export const add = 'add' as const;
export type Add = { readonly tag: typeof add; readonly value: Operands } & _common;
export function Add(value: Operands): Add {
return new VariantImpl(add, value) as Add;
}
export const sub = 'sub' as const;
export type Sub = { readonly tag: typeof sub; readonly value: Operands } & _common;
export function Sub(value: Operands): Sub {
return new VariantImpl(sub, value) as Sub;
}
export const mul = 'mul' as const;
export type Mul = { readonly tag: typeof mul; readonly value: Operands } & _common;
export function Mul(value: Operands): Mul {
return new VariantImpl(mul, value) as Mul;
}
export const div = 'div' as const;
export type Div = { readonly tag: typeof div; readonly value: Operands } & _common;
export function Div(value: Operands): Div {
return new VariantImpl(div, value) as Div;
}
export type _tt = typeof add | typeof sub | typeof mul | typeof div;
export type _vt = Operands | Operands | Operands | Operands;
type _common = Omit<VariantImpl, 'tag' | 'value'>;
export function _ctor(t: _tt, v: _vt): Operation {
return new VariantImpl(t, v) as Operation;
}
class VariantImpl {
private readonly _tag: _tt;
private readonly _value: _vt;
constructor(t: _tt, value: _vt) {
this._tag = t;
this._value = value;
}
get tag(): _tt {
return this._tag;
}
get value(): _vt {
return this._value;
}
isAdd(): this is Add {
return this._tag === Operation.add;
}
isSub(): this is Sub {
return this._tag === Operation.sub;
}
isMul(): this is Mul {
return this._tag === Operation.mul;
}
isDiv(): this is Div {
return this._tag === Operation.div;
}
}
}
export type Operation = Operation.Add | Operation.Sub | Operation.Mul | Operation.Div;
}
export type Types = {
};
export namespace calculator {
export type Operation = Types.Operation;
export type Imports = {
log: (msg: string) => void;
};
export type Exports = {
calc: (o: Operation) => u32;
};
}
}
export namespace example {
export namespace Types.$ {
export const Operands = new $wcm.RecordType<example.Types.Operands>([
['left', $wcm.u32],
['right', $wcm.u32],
]);
export const Operation = new $wcm.VariantType<example.Types.Operation, example.Types.Operation._tt, example.Types.Operation._vt>([['add', Operands], ['sub', Operands], ['mul', Operands], ['div', Operands]], example.Types.Operation._ctor);
}
export namespace Types._ {
export const id = 'vscode:example/types' as const;
export const witName = 'types' as const;
export const types: Map<string, $wcm.GenericComponentModelType> = new Map<string, $wcm.GenericComponentModelType>([
['Operands', $.Operands],
['Operation', $.Operation]
]);
}
export namespace calculator.$ {
export const Operation = Types.$.Operation;
export namespace Imports {
export const log = new $wcm.FunctionType<calculator.Imports['log']>('log',[
['msg', $wcm.wstring],
], undefined);
}
export namespace Exports {
export const calc = new $wcm.FunctionType<calculator.Exports['calc']>('calc',[
['o', Operation],
], $wcm.u32);
}
}
export namespace calculator._ {
export const id = 'vscode:example/calculator' as const;
export const witName = 'calculator' as const;
export type $Root = {
'log': (msg_ptr: i32, msg_len: i32) => void;
}
export namespace Imports {
export const functions: Map<string, $wcm.FunctionType> = new Map([
['log', $.Imports.log]
]);
export const interfaces: Map<string, $wcm.InterfaceType> = new Map<string, $wcm.InterfaceType>([
['Types', Types._]
]);
}
export type Imports = {
'$root': $Root;
};
export namespace Exports {
export const functions: Map<string, $wcm.FunctionType> = new Map([
['calc', $.Exports.calc]
]);
}
export type Exports = {
'calc': (o_Operation_case: i32, o_Operation_0: i32, o_Operation_1: i32) => i32;
};
export function createImports(service: calculator.Imports, context: $wcm.WasmContext): Imports {
const result: Imports = Object.create(null);
result['$root'] = $wcm.Imports.create<$Root>(Imports.functions, undefined, service, context);
return result;
}
export function bindExports(exports: Exports, context: $wcm.WasmContext): calculator.Exports {
const result: calculator.Exports = Object.create(null);
Object.assign(result, $wcm.Exports.bind(Exports.functions, undefined, exports, context));
return result;
}
}
}
export namespace example._ {
export const id = 'vscode:example' as const;
export const witName = 'example' as const;
export const interfaces: Map<string, $wcm.InterfaceType> = new Map<string, $wcm.InterfaceType>([
['Types', Types._]
]);
export const worlds: Map<string, $wcm.WorldType> = new Map<string, $wcm.WorldType>([
['calculator', calculator._]
]);
}

View File

@ -5,9 +5,8 @@
import * as vscode from 'vscode';
import { WasmContext, Memory } from '@vscode/wasm-component-model';
import { example } from './example';
import calculator = example.calculator;
import Types = example.Types;
// Import the code generated by wit2ts
import { Types, calculator } from './calculator';
export async function activate(context: vscode.ExtensionContext): Promise<void> {
// The channel for printing the result.
@ -28,25 +27,32 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
log: (msg: string) => {
log.info(msg);
}
}
};
// The context for the WASM module
const wasmContext: WasmContext.Default = new WasmContext.Default();
// Instantiate the module and create the necessary imports from the service implementation
const instance = await WebAssembly.instantiate(module, calculator._.createImports(service, wasmContext));
// Create the bindings to import the log function into the WASM module
const imports = calculator._.imports.create(service, wasmContext);
// Instantiate the module
const instance = await WebAssembly.instantiate(module, imports);
// Bind the WASM memory to the context
wasmContext.initialize(new Memory.Default(instance.exports));
// Bind the JavaScript Api
const api = calculator._.bindExports(instance.exports as calculator._.Exports, wasmContext);
// Bind the TypeScript Api
const api = calculator._.exports.bind(instance.exports as calculator._.Exports, wasmContext);
context.subscriptions.push(vscode.commands.registerCommand('vscode-samples.wasm-component-model.run', () => {
channel.show();
channel.appendLine('Running calculator example');
channel.appendLine(`Add ${api.calc(Types.Operation.Add({ left: 1, right: 2}))}`);
channel.appendLine(`Sub ${api.calc(Types.Operation.Sub({ left: 10, right: 8 }))}`);
channel.appendLine(`Mul ${api.calc(Types.Operation.Mul({ left: 3, right: 7 }))}`);
channel.appendLine(`Div ${api.calc(Types.Operation.Div({ left: 10, right: 2 }))}`);
const add = Types.Operation.Add({ left: 1, right: 2});
channel.appendLine(`Add ${api.calc(add)}`);
const sub = Types.Operation.Sub({ left: 10, right: 8 });
channel.appendLine(`Sub ${api.calc(sub)}`);
const mul = Types.Operation.Mul({ left: 3, right: 7 });
channel.appendLine(`Mul ${api.calc(mul)}`);
const div = Types.Operation.Div({ left: 10, right: 2 });
channel.appendLine(`Div ${api.calc(div)}`);
}));
}

View File

@ -5,11 +5,11 @@ wit_bindgen::generate!({
world: "calculator",
});
struct MyType;
struct Calculator;
impl Guest for MyType {
impl Guest for Calculator {
fn calc(op: Operation) -> u32 {
fn calc(op: Operation) -> u32 {
log(&format!("Starting calculation: {:?}", op));
let result = match op {
Operation::Add(operands) => operands.left + operands.right,
@ -18,8 +18,9 @@ impl Guest for MyType {
Operation::Div(operands) => operands.left / operands.right,
};
log(&format!("Finished calculation: {:?}", op));
return result;
result
}
}
export!(MyType);
// Export the Calculator to the extension code.
export!(Calculator);

View File

@ -1,15 +1,15 @@
{
"name": "client",
"version": "1.0.0",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "client",
"version": "1.0.0",
"version": "0.1.0",
"license": "MIT",
"dependencies": {
"@vscode/wasm-wasi-lsp": "0.1.0-pre.1"
"@vscode/wasm-wasi-lsp": "0.1.0-pre.3"
},
"devDependencies": {
"@types/node": "^18.14.6",
@ -52,15 +52,15 @@
}
},
"node_modules/@vscode/wasm-wasi-lsp": {
"version": "0.1.0-pre.1",
"resolved": "https://registry.npmjs.org/@vscode/wasm-wasi-lsp/-/wasm-wasi-lsp-0.1.0-pre.1.tgz",
"integrity": "sha512-xB6Jjo7v1JnJsxH18/d1GyhXqi3wqLe20cyvfQQp2XOKzapgGJC+CGL+Hc5w3BubOKwwcbHe6mtaMEKgArrv5A==",
"version": "0.1.0-pre.3",
"resolved": "https://registry.npmjs.org/@vscode/wasm-wasi-lsp/-/wasm-wasi-lsp-0.1.0-pre.3.tgz",
"integrity": "sha512-3qgroKPvPKNcXd8Nl7DQINqSQtAoQ/6dix/X3b2fgdgJyBkISz/gNK5HfLBBC06lFCZlwP5gpaj1nx64No2y3A==",
"engines": {
"node": ">=18.18.2"
},
"peerDependencies": {
"@vscode/wasm-wasi": "0.13.0-pre.1",
"vscode-languageclient": "10.0.0-next.3"
"vscode-languageclient": "10.0.0-next.5"
}
},
"node_modules/ansi-regex": {
@ -260,9 +260,9 @@
}
},
"node_modules/vscode-languageclient": {
"version": "10.0.0-next.3",
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.3.tgz",
"integrity": "sha512-jJhPdZaiELpPRnCUt8kQcF2HJuvzLgeW4HOGc6dp8Je+p08ndueVT4fpSsbly6KiEHr/Ri73tNz0CSfsOye6MA==",
"version": "10.0.0-next.5",
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.5.tgz",
"integrity": "sha512-JIf1WE7fvV0RElFM062bAummI433vcxuFwqoYAp+1zTVhta/jznxkTz1zs3Hbj2tiDfclf0TZ0qCxflAP1mY2Q==",
"peer": true,
"dependencies": {
"minimatch": "^9.0.3",

View File

@ -3,7 +3,7 @@
"description": "The language client",
"author": "Microsoft Corporation",
"license": "MIT",
"version": "1.0.0",
"version": "0.1.0",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/vscode-extension-samples"
@ -22,7 +22,7 @@
"main": "./out/extension",
"browser": "./dist/web/extension",
"dependencies": {
"@vscode/wasm-wasi-lsp": "0.1.0-pre.1"
"@vscode/wasm-wasi-lsp": "0.1.0-pre.3"
},
"devDependencies": {
"@types/vscode": "1.88.0",

View File

@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import { ExtensionContext, Uri, window, workspace } from 'vscode';
import { LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node';
import { ExtensionContext, Uri, window, workspace, commands } from 'vscode';
import { LanguageClient, LanguageClientOptions, ServerOptions, RequestType } from 'vscode-languageclient/node';
import { Wasm, ProcessOptions } from '@vscode/wasm-wasi';
import { createStdioOptions, startServer } from '@vscode/wasm-wasi-lsp';
@ -46,6 +46,17 @@ export async function activate(context: ExtensionContext) {
} catch (error) {
client.error(`Start failed`, error, 'force');
}
type CountFileParams = { folder: string };
const CountFilesRequest = new RequestType<CountFileParams, number, void>('wasm-language-server/countFilesInFolder');
context.subscriptions.push(commands.registerCommand('vscode-samples.wasm-language-server.countFiles', async () => {
// We assume we do have a folder.
const folder = workspace.workspaceFolders![0].uri;
// We need to convert the folder URI to a URI that maps to the mounted WASI file system. This is something
// @vscode/wasm-wasi-lsp does for us.
const result = await client.sendRequest(CountFilesRequest, { folder: client.code2ProtocolConverter.asUri(folder!) });
window.showInformationMessage(`The workspace contains ${result} files.`);
}));
}
export function deactivate() {

View File

@ -27,6 +27,15 @@
"extensionDependencies": [
"ms-vscode.wasm-wasi-core"
],
"contributes": {
"commands": [
{
"command": "vscode-samples.wasm-language-server.countFiles",
"category": "Samples",
"title": "Count Files"
}
]
},
"dependencies": {
},
"devDependencies": {

View File

@ -109,6 +109,15 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "serde"
version = "1.0.197"
@ -159,6 +168,7 @@ dependencies = [
"lsp-types",
"serde",
"serde_json",
"walkdir",
]
[[package]]
@ -219,3 +229,44 @@ dependencies = [
"percent-encoding",
"serde",
]
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -8,5 +8,6 @@ edition = "2021"
[dependencies]
lsp-server = "0.7.6"
lsp-types = "0.95.1"
walkdir = "2"
serde = "1.0.189"
serde_json = "1.0.107"

View File

@ -4,13 +4,11 @@
*--------------------------------------------------------------------------------------------*/
use std::error::Error;
use lsp_types::OneOf;
use lsp_types::{
request::GotoDefinition, GotoDefinitionResponse, InitializeParams, ServerCapabilities,
Location
request::GotoDefinition, GotoDefinitionResponse, InitializeParams,
ServerCapabilities, Location, OneOf
};
use lsp_server::{Connection, ExtractError, Message, Request, RequestId, Response};
use lsp_server::{ Connection, ExtractError, Message, RequestId, Response };
fn main() -> Result<(), Box<dyn Error + Sync + Send>> {
// Note that we must have our logging only write out to stderr.
@ -35,10 +33,7 @@ fn main() -> Result<(), Box<dyn Error + Sync + Send>> {
Ok(())
}
fn main_loop(
connection: Connection,
params: serde_json::Value,
) -> Result<(), Box<dyn Error + Sync + Send>> {
fn main_loop(connection: Connection, params: serde_json::Value) -> Result<(), Box<dyn Error + Sync + Send>> {
let _params: InitializeParams = serde_json::from_value(params).unwrap();
for msg in &connection.receiver {
match msg {
@ -61,7 +56,6 @@ fn main_loop(
Err(err @ ExtractError::JsonError { .. }) => panic!("{err:?}"),
Err(ExtractError::MethodMismatch(req)) => req,
};
// ...
}
Message::Response(resp) => {
eprintln!("got response: {resp:?}");
@ -74,10 +68,10 @@ fn main_loop(
Ok(())
}
fn cast<R>(req: Request) -> Result<(RequestId, R::Params), ExtractError<Request>>
fn cast<R>(req: lsp_server::Request) -> Result<(RequestId, R::Params), ExtractError<lsp_server::Request>>
where
R: lsp_types::request::Request,
R::Params: serde::de::DeserializeOwned,
{
req.extract(R::METHOD)
}
}

View File

@ -0,0 +1 @@
Some text.