lsp_bin/
client.rs

1use std::{collections::HashMap, fmt::Display, path::PathBuf, pin::Pin};
2
3use bevy_ecs::system::Resource;
4use futures::FutureExt;
5use lsp_core::{
6    client::{Client, ClientSync, Resp},
7    prelude::FsTrait,
8};
9use lsp_types::{Diagnostic, MessageType, Url};
10use tokio::fs::{self, read_to_string, write};
11use tracing::info;
12
13pub mod reqwest {
14    pub use reqwest::{
15        header::{HeaderMap, HeaderName, HeaderValue},
16        Error, StatusCode, Url,
17    };
18}
19
20// TODO: use other tmp file location
21#[derive(Debug)]
22pub struct BinFs(PathBuf);
23impl BinFs {
24    pub fn new() -> Self {
25        let mut tmp_dir = std::env::temp_dir();
26        tmp_dir.push("swls");
27        let mut c = 0;
28        tmp_dir.push(c.to_string());
29        while std::fs::exists(&tmp_dir).unwrap_or(false) {
30            tmp_dir.pop();
31            c += 1;
32            tmp_dir.push(c.to_string());
33        }
34        Self(tmp_dir)
35    }
36}
37
38#[tower_lsp::async_trait]
39impl FsTrait for BinFs {
40    fn virtual_url(&self, url: &str) -> Option<lsp_types::Url> {
41        let mut pb = self.0.clone();
42        if let Ok(url) = lsp_types::Url::parse(url) {
43            pb.push(url.path());
44        } else {
45            pb.push(url);
46        }
47        lsp_types::Url::from_file_path(pb).ok()
48    }
49
50    async fn read_file(&self, url: &lsp_types::Url) -> Option<String> {
51        let fp = url.to_file_path().ok()?;
52        let content = read_to_string(fp).await.ok()?;
53        Some(content)
54    }
55
56    async fn write_file(&self, url: &lsp_types::Url, content: &str) -> Option<()> {
57        let fp = url.to_file_path().ok()?;
58        if let Some(parent) = fp.parent() {
59            fs::create_dir_all(parent).await.ok()?;
60        }
61        write(fp, content.as_bytes()).await.ok()
62    }
63}
64
65#[derive(Resource, Clone)]
66pub struct TowerClient {
67    client: tower_lsp::Client,
68    handle: tokio::runtime::Handle,
69}
70impl TowerClient {
71    pub fn new(client: tower_lsp::Client) -> Self {
72        Self {
73            client,
74            handle: tokio::runtime::Handle::current(),
75        }
76    }
77}
78
79impl ClientSync for TowerClient {
80    fn spawn<F: std::future::Future<Output = ()> + Send + 'static>(&self, fut: F) {
81        let handle = std::thread::current();
82        info!("Spawn threaad name {:?}", handle.id());
83        self.handle.spawn(fut);
84        // info!("Should spawn but won't!");
85    }
86
87    fn fetch(
88        &self,
89        url: &str,
90        headers: &HashMap<String, String>,
91    ) -> Pin<Box<dyn Send + std::future::Future<Output = Result<Resp, String>>>> {
92        use tokio::{fs::File, io::AsyncReadExt};
93        use tracing::{debug, error, info};
94        info!("Should fetch, fetching!");
95
96        let m_url = reqwest::Url::parse(url);
97
98        let client = ::reqwest::Client::new();
99        let builder = client.get(url);
100        let builder = headers
101            .into_iter()
102            .fold(builder, |builder, (k, v)| builder.header(k, v));
103
104        return async {
105            let url = m_url.map_err(|_| String::from("invalid url!"))?;
106            info!("Found url {} {}", url.scheme(), url);
107            if url.scheme() == "file" {
108                let mut file = File::open(url.path())
109                    .await
110                    .map_err(|_| format!("File not found {}", url.path()))?;
111                let mut body = String::new();
112                file.read_to_string(&mut body)
113                    .await
114                    .map_err(|_| format!("Failed to read file"))?;
115                let status = 200;
116                let headers = Vec::new();
117                return Ok(Resp {
118                    headers,
119                    body,
120                    status,
121                });
122            }
123
124            debug!("sending blocking");
125            let resp = match builder.send().await {
126                Ok(x) => x,
127                Err(e) => {
128                    error!(error = ?e);
129                    return Err(e.to_string());
130                }
131            };
132
133            let status = resp.status().as_u16();
134            let headers: Vec<_> = resp
135                .headers()
136                .iter()
137                .flat_map(|(h, v)| {
138                    if let Ok(value) = v.to_str() {
139                        Some((h.to_string(), value.to_string()))
140                    } else {
141                        None
142                    }
143                })
144                .collect();
145            debug!("got resp");
146            let body = resp.text().await.unwrap();
147
148            Ok(Resp {
149                headers,
150                body,
151                status,
152            })
153        }
154        .boxed();
155    }
156}
157
158#[tower_lsp::async_trait]
159impl Client for TowerClient {
160    async fn log_message<M: Display + Sync + Send + 'static>(&self, ty: MessageType, msg: M) -> () {
161        self.client.log_message(ty, msg).await;
162    }
163
164    async fn publish_diagnostics(
165        &self,
166        uri: Url,
167        diags: Vec<Diagnostic>,
168        version: Option<i32>,
169    ) -> () {
170        self.client.publish_diagnostics(uri, diags, version).await;
171    }
172}