1#![doc(
2 html_logo_url = "https://ajuvercr.github.io/semantic-web-lsp/assets/icons/favicon.png",
3 html_favicon_url = "https://ajuvercr.github.io/semantic-web-lsp/assets/icons/favicon.ico"
4)]
5mod client;
6mod fetch;
7
8use std::io::Write;
9
10use bevy_ecs::{system::Resource, world::World};
11use client::{WebClient, WebFs};
12use futures::{channel::mpsc::unbounded, stream::TryStreamExt, StreamExt};
13use lsp_core::prelude::*;
14use lsp_types::SemanticTokenType;
15use tower_lsp::{LspService, Server};
16use tracing::level_filters::LevelFilter;
17use wasm_bindgen::{prelude::*, JsCast};
18use wasm_bindgen_futures::stream::JsStream;
19
20#[wasm_bindgen]
21extern "C" {
22 #[wasm_bindgen()]
23 pub fn info(string: &str);
24 pub fn debug(string: &str);
25}
26
27struct LogItWriter;
28impl LogItWriter {
29 fn new() -> Self {
30 debug("building self");
31 LogItWriter
32 }
33}
34impl Write for LogItWriter {
35 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
36 match std::str::from_utf8(buf) {
37 Ok(st) => {
38 debug(st);
39 }
40 Err(e) => debug(&format!("Invalid string logged {:?}", e)),
41 }
42
43 Ok(buf.len())
44 }
45
46 fn flush(&mut self) -> std::io::Result<()> {
47 Ok(())
48 }
49}
50
51fn setup_global_subscriber() {
52 use tracing_subscriber::prelude::*;
53
54 let fmt_layer = tracing_subscriber::fmt::layer()
55 .pretty()
56 .with_ansi(false)
57 .without_time() .with_writer(std::sync::Mutex::new(LogItWriter::new()))
59 .with_filter(LevelFilter::DEBUG);
60
61 tracing_subscriber::registry().with(fmt_layer).init();
62}
63
64fn setup_world<C: Client + ClientSync + Resource + Clone>(
65 client: C,
66 tower_client: &tower_lsp::Client,
67) -> (CommandSender, Vec<SemanticTokenType>) {
68 let mut world = World::new();
69
70 setup_schedule_labels::<C>(&mut world);
71
72 let (publisher, mut rx) = DiagnosticPublisher::new();
73 world.insert_resource(publisher);
74
75 let c = client.clone();
76 client.spawn(async move {
77 while let Some(x) = rx.next().await {
78 c.publish_diagnostics(x.uri, x.diagnostics, x.version).await;
79 }
80 });
81
82 lang_turtle::setup_world(&mut world);
83 lang_jsonld::setup_world(&mut world);
84 lang_sparql::setup_world(&mut world);
85
86 let (tx, mut rx) = unbounded();
87 let sender = CommandSender(tx);
88 world.insert_resource(sender.clone());
89 world.insert_resource(client.clone());
90 world.insert_resource(WebFs::new(tower_client));
91
92 let r = world.resource::<SemanticTokensDict>();
93 let mut semantic_tokens: Vec<_> = (0..r.0.len()).map(|_| SemanticTokenType::KEYWORD).collect();
94 r.0.iter()
95 .for_each(|(k, v)| semantic_tokens[*v] = k.clone());
96
97 client.spawn(async move {
98 while let Some(mut x) = rx.next().await {
99 world.commands().append(&mut x);
100 world.flush_commands();
101 }
102 });
103
104 (sender, semantic_tokens)
105}
106
107#[wasm_bindgen]
108pub struct ServerConfig {
109 into_server: js_sys::AsyncIterator,
110 from_server: web_sys::WritableStream,
111}
112
113#[wasm_bindgen]
114impl ServerConfig {
115 #[wasm_bindgen(constructor)]
116 pub fn new(into_server: js_sys::AsyncIterator, from_server: web_sys::WritableStream) -> Self {
117 Self {
118 into_server,
119 from_server,
120 }
121 }
122}
123
124#[wasm_bindgen]
130pub async fn serve(config: ServerConfig) -> Result<(), JsValue> {
131 console_error_panic_hook::set_once();
132
133 web_sys::console::log_1(&"server::serve".into());
134
135 setup_global_subscriber();
136
137 info("string string string");
138
139 let ServerConfig {
140 into_server,
141 from_server,
142 } = config;
143
144 let input = JsStream::from(into_server);
145 let input = input
146 .map_ok(|value| {
147 value
148 .dyn_into::<js_sys::Uint8Array>()
149 .expect("could not cast stream item to Uint8Array")
150 .to_vec()
151 })
152 .map_err(|_err| std::io::Error::from(std::io::ErrorKind::Other))
153 .into_async_read();
154
155 let output = JsCast::unchecked_into::<wasm_streams::writable::sys::WritableStream>(from_server);
156 let output = wasm_streams::WritableStream::from_raw(output);
157 let output = output.try_into_async_write().map_err(|err| err.0)?;
158
159 let (service, socket) = LspService::build(|client| {
160 let (sender, rt) = setup_world(WebClient::new(client.clone()), &client);
161 Backend::new(sender, client, rt)
162 })
163 .finish();
164
165 debug("Testing logit, I'm serve 1");
166 Server::new(input, output, socket).serve(service).await;
167
168 Ok(())
169}
170
171#[wasm_bindgen]
172pub async fn serve2(config: ServerConfig) -> Result<(), JsValue> {
173 console_error_panic_hook::set_once();
174
175 web_sys::console::log_1(&"server::serve".into());
176
177 setup_global_subscriber();
178
179 tracing::info!("Hallo I'm here!");
180
181 let ServerConfig {
182 into_server,
183 from_server,
184 } = config;
185
186 let input = JsStream::from(into_server);
187 let input = input
188 .map_ok(|value| {
189 value
190 .dyn_into::<js_sys::Uint8Array>()
191 .expect("could not cast stream item to Uint8Array")
192 .to_vec()
193 })
194 .map_err(|_err| std::io::Error::from(std::io::ErrorKind::Other))
195 .into_async_read();
196
197 let output = JsCast::unchecked_into::<wasm_streams::writable::sys::WritableStream>(from_server);
198 let output = wasm_streams::WritableStream::from_raw(output);
199 let output = output.try_into_async_write().map_err(|err| err.0)?;
200 let (service, socket) = LspService::build(|client| {
201 let (sender, rt) = setup_world(WebClient::new(client.clone()), &client);
202 Backend::new(sender, client, rt)
203 })
204 .finish();
205 debug("Testing logit, I'm serve 2");
206
207 Server::new(input, output, socket).serve(service).await;
208
209 Ok(())
210}