1 //! Driver for rust-analyzer.
3 //! Based on cli flags, either spawns an LSP server, or runs a batch analysis
8 use std::{convert::TryFrom, env, fs, path::Path, process};
10 use lsp_server::Connection;
11 use project_model::ProjectManifest;
13 cli::{self, AnalysisStatsCmd},
16 lsp_ext::supports_utf8,
21 #[cfg(all(feature = "mimalloc"))]
23 static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
25 #[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
27 static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
30 if std::env::var("RA_RUSTC_WRAPPER").is_ok() {
31 let mut args = std::env::args_os();
32 let _me = args.next().unwrap();
33 let rustc = args.next().unwrap();
34 let code = match rustc_wrapper::run_rustc_skipping_cargo_checking(rustc, args.collect()) {
35 Ok(rustc_wrapper::ExitCode(code)) => code.unwrap_or(102),
44 if let Err(err) = try_main() {
45 log::error!("Unexpected error: {}", err);
51 fn try_main() -> Result<()> {
52 let flags = flags::RustAnalyzer::from_env()?;
54 #[cfg(debug_assertions)]
55 if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() {
63 setup_logging(flags.log_file.as_deref(), flags.no_log_buffering)?;
64 let verbosity = flags.verbosity();
66 match flags.subcommand {
67 flags::RustAnalyzerCmd::LspServer(cmd) => {
68 if cmd.print_config_schema {
69 println!("{:#}", Config::json_schema());
73 println!("rust-analyzer {}", env!("REV"));
77 println!("{}", flags::RustAnalyzer::HELP);
82 flags::RustAnalyzerCmd::ProcMacro(_) => proc_macro_srv::cli::run()?,
83 flags::RustAnalyzerCmd::Parse(cmd) => cli::parse(cmd.no_dump)?,
84 flags::RustAnalyzerCmd::Symbols(_) => cli::symbols()?,
85 flags::RustAnalyzerCmd::Highlight(cmd) => cli::highlight(cmd.rainbow)?,
86 flags::RustAnalyzerCmd::AnalysisStats(cmd) => AnalysisStatsCmd {
87 randomize: cmd.randomize,
88 parallel: cmd.parallel,
89 memory_usage: cmd.memory_usage,
91 with_deps: cmd.with_deps,
92 no_sysroot: cmd.no_sysroot,
94 load_output_dirs: cmd.load_output_dirs,
95 with_proc_macro: cmd.with_proc_macro,
96 skip_inference: cmd.skip_inference,
100 flags::RustAnalyzerCmd::Diagnostics(cmd) => {
101 cli::diagnostics(&cmd.path, cmd.load_output_dirs, cmd.with_proc_macro)?
103 flags::RustAnalyzerCmd::Ssr(cmd) => cli::apply_ssr_rules(cmd.rule)?,
104 flags::RustAnalyzerCmd::Search(cmd) => cli::search_for_patterns(cmd.pattern, cmd.debug)?,
109 fn setup_logging(log_file: Option<&Path>, no_buffering: bool) -> Result<()> {
110 env::set_var("RUST_BACKTRACE", "short");
112 let log_file = match log_file {
114 if let Some(parent) = path.parent() {
115 let _ = fs::create_dir_all(parent);
117 Some(fs::File::create(path)?)
121 let filter = env::var("RA_LOG").ok();
122 logger::Logger::new(log_file, no_buffering, filter.as_deref()).install();
124 tracing_setup::setup_tracing()?;
132 use tracing::subscriber;
133 use tracing_subscriber::layer::SubscriberExt;
134 use tracing_subscriber::EnvFilter;
135 use tracing_subscriber::Registry;
136 use tracing_tree::HierarchicalLayer;
138 pub(crate) fn setup_tracing() -> super::Result<()> {
139 let filter = EnvFilter::from_env("CHALK_DEBUG");
140 let layer = HierarchicalLayer::default()
141 .with_indent_lines(true)
143 .with_indent_amount(2)
144 .with_writer(std::io::stderr);
145 let subscriber = Registry::default().with(filter).with(layer);
146 subscriber::set_global_default(subscriber)?;
151 fn run_server() -> Result<()> {
152 log::info!("server version {} will start", env!("REV"));
154 let (connection, io_threads) = Connection::stdio();
156 let (initialize_id, initialize_params) = connection.initialize_start()?;
157 log::info!("InitializeParams: {}", initialize_params);
158 let initialize_params =
159 from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;
161 let server_capabilities = rust_analyzer::server_capabilities(&initialize_params.capabilities);
163 let initialize_result = lsp_types::InitializeResult {
164 capabilities: server_capabilities,
165 server_info: Some(lsp_types::ServerInfo {
166 name: String::from("rust-analyzer"),
167 version: Some(String::from(env!("REV"))),
169 offset_encoding: if supports_utf8(&initialize_params.capabilities) {
170 Some("utf-8".to_string())
176 let initialize_result = serde_json::to_value(initialize_result).unwrap();
178 connection.initialize_finish(initialize_id, initialize_result)?;
180 if let Some(client_info) = initialize_params.client_info {
181 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
185 let root_path = match initialize_params
187 .and_then(|it| it.to_file_path().ok())
188 .and_then(|it| AbsPathBuf::try_from(it).ok())
192 let cwd = env::current_dir()?;
193 AbsPathBuf::assert(cwd)
197 let mut config = Config::new(root_path, initialize_params.capabilities);
198 if let Some(json) = initialize_params.initialization_options {
202 if config.linked_projects().is_empty() {
203 let workspace_roots = initialize_params
208 .filter_map(|it| it.uri.to_file_path().ok())
209 .filter_map(|it| AbsPathBuf::try_from(it).ok())
212 .filter(|workspaces| !workspaces.is_empty())
213 .unwrap_or_else(|| vec![config.root_path.clone()]);
215 let discovered = ProjectManifest::discover_all(&workspace_roots);
216 log::info!("discovered projects: {:?}", discovered);
217 if discovered.is_empty() && config.detached_files().is_empty() {
218 log::error!("failed to find any projects in {:?}", workspace_roots);
221 config.discovered_projects = Some(discovered);
227 rust_analyzer::main_loop(config, connection)?;
230 log::info!("server did shut down");