]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/src/bin/main.rs
1d6e5478bc4d38969dd64cb20edc45747d1556e6
[rust.git] / crates / rust-analyzer / src / bin / main.rs
1 //! Driver for rust-analyzer.
2 //!
3 //! Based on cli flags, either spawns an LSP server, or runs a batch analysis
4 mod args;
5 mod logger;
6
7 use std::{convert::TryFrom, env, fs, path::PathBuf, process};
8
9 use lsp_server::Connection;
10 use project_model::ProjectManifest;
11 use rust_analyzer::{cli, config::Config, from_json, Result};
12 use vfs::AbsPathBuf;
13
14 #[cfg(all(feature = "mimalloc"))]
15 #[global_allocator]
16 static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
17
18 #[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
19 #[global_allocator]
20 static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
21
22 fn main() {
23     if let Err(err) = try_main() {
24         eprintln!("{}", err);
25         process::exit(101);
26     }
27 }
28
29 fn try_main() -> Result<()> {
30     let args = args::Args::parse()?;
31     setup_logging(args.log_file)?;
32     match args.command {
33         args::Command::RunServer => run_server()?,
34         args::Command::PrintConfigSchema => {
35             println!("{:#}", Config::json_schema());
36         }
37         args::Command::ProcMacro => proc_macro_srv::cli::run()?,
38
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)?
46         }
47         args::Command::Ssr { rules } => {
48             cli::apply_ssr_rules(rules)?;
49         }
50         args::Command::StructuredSearch { patterns, debug_snippet } => {
51             cli::search_for_patterns(patterns, debug_snippet)?;
52         }
53         args::Command::Version => println!("rust-analyzer {}", env!("REV")),
54         args::Command::Help => {}
55     }
56     Ok(())
57 }
58
59 fn setup_logging(log_file: Option<PathBuf>) -> Result<()> {
60     env::set_var("RUST_BACKTRACE", "short");
61
62     let log_file = match log_file {
63         Some(path) => {
64             if let Some(parent) = path.parent() {
65                 let _ = fs::create_dir_all(parent);
66             }
67             Some(fs::File::create(path)?)
68         }
69         None => None,
70     };
71     let filter = env::var("RA_LOG").ok();
72     logger::Logger::new(log_file, filter.as_deref()).install();
73
74     tracing_setup::setup_tracing()?;
75
76     profile::init();
77
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)
82             }
83             log::error!("assertion failed at {}: {}", loc, args)
84         });
85     }
86
87     Ok(())
88 }
89
90 mod tracing_setup {
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;
96
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)
101             .with_ansi(false)
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)?;
106         Ok(())
107     }
108 }
109
110 fn run_server() -> Result<()> {
111     log::info!("server will start");
112
113     let (connection, io_threads) = Connection::stdio();
114
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)?;
119
120     let server_capabilities = rust_analyzer::server_capabilities(&initialize_params.capabilities);
121
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"))),
127         }),
128     };
129
130     let initialize_result = serde_json::to_value(initialize_result).unwrap();
131
132     connection.initialize_finish(initialize_id, initialize_result)?;
133
134     if let Some(client_info) = initialize_params.client_info {
135         log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
136     }
137
138     let config = {
139         let root_path = match initialize_params
140             .root_uri
141             .and_then(|it| it.to_file_path().ok())
142             .and_then(|it| AbsPathBuf::try_from(it).ok())
143         {
144             Some(it) => it,
145             None => {
146                 let cwd = env::current_dir()?;
147                 AbsPathBuf::assert(cwd)
148             }
149         };
150
151         let mut config = Config::new(root_path, initialize_params.capabilities);
152         if let Some(json) = initialize_params.initialization_options {
153             config.update(json);
154         }
155
156         if config.linked_projects().is_empty() {
157             let workspace_roots = initialize_params
158                 .workspace_folders
159                 .map(|workspaces| {
160                     workspaces
161                         .into_iter()
162                         .filter_map(|it| it.uri.to_file_path().ok())
163                         .filter_map(|it| AbsPathBuf::try_from(it).ok())
164                         .collect::<Vec<_>>()
165                 })
166                 .filter(|workspaces| !workspaces.is_empty())
167                 .unwrap_or_else(|| vec![config.root_path.clone()]);
168
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);
173             }
174
175             config.discovered_projects = Some(discovered);
176         }
177
178         config
179     };
180
181     rust_analyzer::main_loop(config, connection)?;
182
183     io_threads.join()?;
184     log::info!("server did shut down");
185     Ok(())
186 }