#![deny(clippy::unused_async)]
#![deny(clippy::await_holding_invalid_type)]
use std::path::Path;
use std::time::Duration;
use tower_lsp_server::{LspService, Server};
use tracing_subscriber::fmt::writer::MakeWriterExt;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{EnvFilter, fmt as tracing_fmt};
use odoo_lsp::backend::Backend;
use odoo_lsp::index::Interner;
use odoo_lsp::utils::CatchPanic;
mod cli;
#[cfg(doc)]
pub use odoo_lsp::*;
#[cfg(unix)]
mod stdio;
fn main() {
let args = std::env::args().collect::<Vec<_>>();
let args = parse_args_and_init_logger(&args);
let mut threads = args.threads.unwrap_or(4);
match std::thread::available_parallelism() {
Ok(value) if value.get() <= 4 => {
threads = 1usize.max(threads / 2);
}
_ => {}
}
let threads = threads.max(1);
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(threads)
.enable_all()
.build()
.expect("failed to build runtime");
rt.block_on(async move {
if cli::run(args).await {
return;
}
#[cfg(unix)]
let (stdin, stdout) = (
stdio::PipeStdin::lock_tokio().unwrap(),
stdio::PipeStdout::lock_tokio().unwrap(),
);
#[cfg(not(unix))]
let (stdin, stdout) = (tokio::io::stdin(), tokio::io::stdout());
let (service, socket) = LspService::build(Backend::new)
.custom_method("odoo-lsp/debug/usage", |_: &Backend| async move {
Ok(Interner::report_usage())
})
.custom_method("odoo-lsp/inspect-type", Backend::debug_inspect_type)
.finish();
let service = tower::ServiceBuilder::new()
.layer(tower::timeout::TimeoutLayer::new(Duration::from_secs(30)))
.layer_fn(CatchPanic)
.service(service);
Server::new(stdin, stdout, socket)
.concurrency_level(2)
.serve(service)
.await;
})
}
fn parse_args_and_init_logger(args: &[String]) -> cli::Args<'_> {
let args = args.iter().skip(1).map(String::as_str).collect::<Vec<_>>();
let args = cli::parse_args(&args[..]);
let outlog = std::env::var("ODOO_LSP_LOG").ok().and_then(|var| {
let path = match var.as_str() {
#[cfg(unix)]
"1" => Path::new("/tmp/odoo_lsp.log"),
_ => Path::new(&var),
};
std::fs::OpenOptions::new().create(true).append(true).open(path).ok()
});
let registry = tracing_subscriber::registry().with(EnvFilter::from_default_env());
let layer = tracing_fmt::layer()
.without_time()
.with_writer(std::io::stderr)
.with_file(true)
.with_line_number(true)
.with_target(true);
match args.log_format {
cli::LogFormat::Compact => {
let layer = layer.compact();
match outlog {
Some(outlog) => registry.with(layer.map_writer(|stderr| stderr.and(outlog))).init(),
None => registry.with(layer).init(),
}
}
cli::LogFormat::Json => {
let layer = layer
.event_format(tracing_fmt::format().json())
.fmt_fields(tracing_fmt::format::JsonFields::new());
match outlog {
Some(outlog) => registry.with(layer.map_writer(|stderr| stderr.and(outlog))).init(),
None => registry.with(layer).init(),
}
}
}
args
}