owl_ms_language_server/
debugging.rs1use 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
19pub fn log_to_client(client: &Client, msg: String) {
21 let c = client.clone();
22 task::spawn(async move {
23 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}