Skip to main content

owl_ms_language_server/
debugging.rs

1use log::{info, LevelFilter};
2use std::io::Write;
3use std::{env, fs::File};
4use tokio::task;
5use tower_lsp::{lsp_types::MessageType, Client, LspService};
6
7use crate::Backend;
8
9pub fn timeit<F: FnOnce() -> T, T>(name: &str, f: F) -> T {
10    use std::time::Instant;
11    let start = Instant::now();
12    let result = f();
13    let end = Instant::now();
14    let duration = end.duration_since(start);
15    info!("⏲ {name} took {duration:?}");
16    result
17}
18
19/// Write a log message to the LSP client. The [`msg`] can include log types, they will get converted.
20pub fn log_to_client(client: &Client, msg: String) {
21    let c = client.clone();
22    task::spawn(async move {
23        // Reformat the log message
24        let mut msg = msg;
25        let mut message_type = MessageType::LOG;
26        for (delimiter, type_) in [
27            ("ERROR", MessageType::ERROR),
28            ("WARN", MessageType::WARNING),
29            ("INFO", MessageType::INFO),
30        ] {
31            if let Some((_, m)) = msg.split_once(delimiter) {
32                msg = m.to_string();
33                message_type = type_;
34            }
35        }
36
37        c.log_message(message_type, msg.trim_end()).await;
38    });
39}
40
41pub struct BackendLogger {
42    pub client: Client,
43    pub client_log_line: String,
44    pub file: Option<File>,
45}
46
47impl BackendLogger {
48    #[must_use]
49    pub fn new(client: Client, file: Option<File>) -> Self {
50        Self {
51            client,
52            client_log_line: String::new(),
53            file,
54        }
55    }
56}
57
58impl Write for BackendLogger {
59    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
60        let str = String::from_utf8_lossy(buf);
61        self.client_log_line.push_str(&str);
62
63        if self.client_log_line.ends_with('\n') {
64            log_to_client(&self.client, self.client_log_line.clone());
65            self.client_log_line.clear();
66        }
67
68        if let Some(f) = &mut self.file {
69            f.write_all(buf).unwrap_or_else(|err| {
70                log_to_client(&self.client, format!("Could not log to file. {err}"));
71            });
72        }
73        Ok(buf.len())
74    }
75
76    fn flush(&mut self) -> std::io::Result<()> {
77        Ok(())
78    }
79}
80
81pub fn init_logging(service: &LspService<Backend>) {
82    let mut log_file_path = env::temp_dir();
83    log_file_path.push("owl-ms-lanugage-server.log");
84    let log_file_path = log_file_path.as_path();
85
86    simple_logging::log_to(
87        BackendLogger::new(
88            service.inner().client.clone(),
89            File::create(log_file_path).ok(),
90        ),
91        #[cfg(debug_assertions)]
92        LevelFilter::Debug,
93        #[cfg(not(debug_assertions))]
94        LevelFilter::Info,
95    );
96}