mirror of
https://github.com/silvanshade/tower-lsp-web-demo.git
synced 2025-01-22 11:47:51 +00:00
Use AsyncIterator for input; works in all browsers
This commit is contained in:
parent
95fd05ee6b
commit
f3f2f1cecc
4 changed files with 70 additions and 30 deletions
|
@ -7,8 +7,6 @@
|
|||
|
||||
## Building
|
||||
|
||||
NOTE: this example uses [ReadableByteStreamController](https://developer.mozilla.org/en-US/docs/Web/API/ReadableByteStreamController#browser_compatibility) which, as of writing this, is only supported yet on chromium based browsers.
|
||||
|
||||
```sh
|
||||
cargo install wasm-bindgen-cli --version 0.2.80
|
||||
cd server
|
||||
|
|
|
@ -1,30 +1,56 @@
|
|||
class LspStdin {
|
||||
static create(stdin: HTMLTextAreaElement, sendButton: HTMLButtonElement): ReadableStream {
|
||||
static async *create(
|
||||
stdin: HTMLTextAreaElement,
|
||||
sendButton: HTMLButtonElement
|
||||
): AsyncGenerator<Uint8Array, never, void> {
|
||||
const encoder = new TextEncoder();
|
||||
return new ReadableStream({
|
||||
type: "bytes" as any,
|
||||
async start(controller) {
|
||||
while (true) {
|
||||
await new Promise<void>((resolve) => {
|
||||
sendButton.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
const payload = stdin.value;
|
||||
const message = `Content-Length: ${payload.length}\r\n\r\n${payload}`;
|
||||
const bytes = encoder.encode(message);
|
||||
controller.enqueue(bytes);
|
||||
stdin.value = "";
|
||||
resolve();
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
while (true) {
|
||||
const bytes = await new Promise<Uint8Array>((resolve) => {
|
||||
sendButton.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
const payload = stdin.value;
|
||||
const message = `Content-Length: ${payload.length}\r\n\r\n${payload}`;
|
||||
stdin.value = "";
|
||||
resolve(encoder.encode(message));
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
yield bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: unused ReadableByteStream based implementation. See comments in server/src/lib.rs.
|
||||
//
|
||||
// class LspStdin {
|
||||
// static create(stdin: HTMLTextAreaElement, sendButton: HTMLButtonElement): ReadableStream {
|
||||
// const encoder = new TextEncoder();
|
||||
// return new ReadableStream({
|
||||
// type: "bytes" as any,
|
||||
// async start(controller) {
|
||||
// while (true) {
|
||||
// await new Promise<void>((resolve) => {
|
||||
// sendButton.addEventListener(
|
||||
// "click",
|
||||
// () => {
|
||||
// const payload = stdin.value;
|
||||
// const message = `Content-Length: ${payload.length}\r\n\r\n${payload}`;
|
||||
// const bytes = encoder.encode(message);
|
||||
// controller.enqueue(bytes);
|
||||
// stdin.value = "";
|
||||
// resolve();
|
||||
// },
|
||||
// { once: true }
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
class LspStdout {
|
||||
static create(stdout: HTMLTextAreaElement): WritableStream {
|
||||
const decoder = new TextDecoder();
|
||||
|
|
|
@ -11,9 +11,11 @@ crate-type = ["cdylib", "rlib"]
|
|||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.21"
|
||||
js-sys = "0.3.57"
|
||||
tower-lsp = { version = "0.17.0", default-features = false }
|
||||
wasm-bindgen = "0.2.80"
|
||||
wasm-bindgen-futures = "0.4.30"
|
||||
wasm-bindgen-futures = { version = "0.4.30", features = ["futures-core-03-stream"] }
|
||||
wasm-streams = "0.2.3"
|
||||
web-sys = { version = "0.3.57", features = [ "console", "ReadableStream", "WritableStream" ] }
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#![cfg(web_sys_unstable_apis)]
|
||||
|
||||
use futures::stream::TryStreamExt;
|
||||
use js_sys::Uint8Array;
|
||||
use tower_lsp::{jsonrpc, lsp_types::*, LanguageServer, LspService, Server};
|
||||
use wasm_bindgen::{prelude::*, JsCast};
|
||||
use wasm_bindgen_futures::stream::JsStream;
|
||||
|
||||
struct LspServer {
|
||||
}
|
||||
|
@ -21,16 +24,27 @@ impl LanguageServer for LspServer {
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE: we don't use web_sys::ReadableStream for input here because on the
|
||||
// browser side we need to use a ReadableByteStreamController to construct it
|
||||
// and so far only Chromium-based browsers support that functionality.
|
||||
|
||||
// NOTE: input needs to be an AsyncIterator<Uint8Array> specifically
|
||||
#[wasm_bindgen]
|
||||
// NOTE: input needs to be a ReadableByteStream specifically
|
||||
pub async fn serve(input: web_sys::ReadableStream, output: web_sys::WritableStream) -> Result<(), JsValue> {
|
||||
pub async fn serve(input: js_sys::AsyncIterator, output: web_sys::WritableStream) -> Result<(), JsValue> {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
web_sys::console::log_1(&"server::serve".into());
|
||||
|
||||
let input = JsCast::unchecked_into::<wasm_streams::readable::sys::ReadableStream>(input);
|
||||
let input = wasm_streams::ReadableStream::from_raw(input);
|
||||
let input = input.try_into_async_read().map_err(|err| err.0)?;
|
||||
let input = JsStream::from(input);
|
||||
let input = input
|
||||
.map_ok(|value| {
|
||||
value
|
||||
.dyn_into::<Uint8Array>()
|
||||
.expect("could not cast stream item to Uint8Array")
|
||||
.to_vec()
|
||||
})
|
||||
.map_err(|_err| std::io::Error::from(std::io::ErrorKind::Other))
|
||||
.into_async_read();
|
||||
|
||||
let output = JsCast::unchecked_into::<wasm_streams::writable::sys::WritableStream>(output);
|
||||
let output = wasm_streams::WritableStream::from_raw(output);
|
||||
|
|
Loading…
Reference in a new issue