1 //! Driver for rust-analyzer.
3 //! Based on cli flags, either spawns an LSP server, or runs a batch analysis
7 use std::{convert::TryFrom, env, fs, path::PathBuf, process};
9 use lsp_server::Connection;
10 use project_model::ProjectManifest;
11 use rust_analyzer::{cli, config::Config, from_json, Result};
14 #[cfg(all(feature = "mimalloc"))]
16 static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
18 #[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
20 static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
23 if let Err(err) = try_main() {
29 fn try_main() -> Result<()> {
30 let args = args::Args::parse()?;
31 setup_logging(args.log_file)?;
33 args::Command::RunServer => run_server()?,
34 args::Command::PrintConfigSchema => {
35 println!("{:#}", Config::json_schema());
37 args::Command::ProcMacro => proc_macro_srv::cli::run()?,
39 args::Command::Parse { no_dump } => cli::parse(no_dump)?,
40 args::Command::Symbols => cli::symbols()?,
41 args::Command::Highlight { rainbow } => cli::highlight(rainbow)?,
42 args::Command::AnalysisStats(cmd) => cmd.run(args.verbosity)?,
43 args::Command::Bench(cmd) => cmd.run(args.verbosity)?,
44 args::Command::Diagnostics { path, load_output_dirs, with_proc_macro } => {
45 cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro)?
47 args::Command::Ssr { rules } => {
48 cli::apply_ssr_rules(rules)?;
50 args::Command::StructuredSearch { patterns, debug_snippet } => {
51 cli::search_for_patterns(patterns, debug_snippet)?;
53 args::Command::Version => println!("rust-analyzer {}", env!("REV")),
54 args::Command::Help => {}
59 fn setup_logging(log_file: Option<PathBuf>) -> Result<()> {
60 env::set_var("RUST_BACKTRACE", "short");
62 let log_file = match log_file {
64 if let Some(parent) = path.parent() {
65 let _ = fs::create_dir_all(parent);
67 Some(fs::File::create(path)?)
71 let filter = env::var("RA_LOG").ok();
72 logger::Logger::new(log_file, filter.as_deref()).install();
74 tracing_setup::setup_tracing()?;
78 if !cfg!(debug_assertions) {
79 stdx::set_assert_hook(|loc, args| {
80 if env::var("RA_PROFILE").is_ok() {
81 panic!("assertion failed at {}: {}", loc, args)
83 log::error!("assertion failed at {}: {}", loc, args)
91 use tracing::subscriber;
92 use tracing_subscriber::layer::SubscriberExt;
93 use tracing_subscriber::EnvFilter;
94 use tracing_subscriber::Registry;
95 use tracing_tree::HierarchicalLayer;
97 pub(crate) fn setup_tracing() -> super::Result<()> {
98 let filter = EnvFilter::from_env("CHALK_DEBUG");
99 let layer = HierarchicalLayer::default()
100 .with_indent_lines(true)
102 .with_indent_amount(2)
103 .with_writer(std::io::stderr);
104 let subscriber = Registry::default().with(filter).with(layer);
105 subscriber::set_global_default(subscriber)?;
110 fn run_server() -> Result<()> {
111 log::info!("server will start");
113 let (connection, io_threads) = Connection::stdio();
115 let (initialize_id, initialize_params) = connection.initialize_start()?;
116 log::info!("InitializeParams: {}", initialize_params);
117 let initialize_params =
118 from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;
120 let server_capabilities = rust_analyzer::server_capabilities(&initialize_params.capabilities);
122 let initialize_result = lsp_types::InitializeResult {
123 capabilities: server_capabilities,
124 server_info: Some(lsp_types::ServerInfo {
125 name: String::from("rust-analyzer"),
126 version: Some(String::from(env!("REV"))),
130 let initialize_result = serde_json::to_value(initialize_result).unwrap();
132 connection.initialize_finish(initialize_id, initialize_result)?;
134 if let Some(client_info) = initialize_params.client_info {
135 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
139 let root_path = match initialize_params
141 .and_then(|it| it.to_file_path().ok())
142 .and_then(|it| AbsPathBuf::try_from(it).ok())
146 let cwd = env::current_dir()?;
147 AbsPathBuf::assert(cwd)
151 let mut config = Config::new(root_path, initialize_params.capabilities);
152 if let Some(json) = initialize_params.initialization_options {
156 if config.linked_projects().is_empty() {
157 let workspace_roots = initialize_params
162 .filter_map(|it| it.uri.to_file_path().ok())
163 .filter_map(|it| AbsPathBuf::try_from(it).ok())
166 .filter(|workspaces| !workspaces.is_empty())
167 .unwrap_or_else(|| vec![config.root_path.clone()]);
169 let discovered = ProjectManifest::discover_all(&workspace_roots);
170 log::info!("discovered projects: {:?}", discovered);
171 if discovered.is_empty() {
172 log::error!("failed to find any projects in {:?}", workspace_roots);
175 config.discovered_projects = Some(discovered);
181 rust_analyzer::main_loop(config, connection)?;
184 log::info!("server did shut down");