diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index c9e0951f829..ace07cc50d9 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -8,13 +8,7 @@ use std::{convert::TryFrom, env, fs, path::Path, process}; use lsp_server::Connection; use project_model::ProjectManifest; -use rust_analyzer::{ - cli::{self, flags, AnalysisStatsCmd}, - config::Config, - from_json, - lsp_ext::supports_utf8, - Result, -}; +use rust_analyzer::{cli::flags, config::Config, from_json, lsp_ext::supports_utf8, Result}; use vfs::AbsPathBuf; #[cfg(all(feature = "mimalloc"))] @@ -85,29 +79,14 @@ fn try_main() -> Result<()> { } run_server()? } - flags::RustAnalyzerCmd::ProcMacro(_) => proc_macro_srv::cli::run()?, - flags::RustAnalyzerCmd::Parse(cmd) => cli::parse(cmd.no_dump)?, - flags::RustAnalyzerCmd::Symbols(_) => cli::symbols()?, - flags::RustAnalyzerCmd::Highlight(cmd) => cli::highlight(cmd.rainbow)?, - flags::RustAnalyzerCmd::AnalysisStats(cmd) => AnalysisStatsCmd { - randomize: cmd.randomize, - parallel: cmd.parallel, - memory_usage: cmd.memory_usage, - only: cmd.only, - with_deps: cmd.with_deps, - no_sysroot: cmd.no_sysroot, - path: cmd.path, - enable_build_scripts: !cmd.disable_build_scripts, - enable_proc_macros: !cmd.disable_proc_macros, - skip_inference: cmd.skip_inference, - } - .run(verbosity)?, - - flags::RustAnalyzerCmd::Diagnostics(cmd) => { - cli::diagnostics(&cmd.path, !cmd.disable_build_scripts, !cmd.disable_proc_macros)? - } - flags::RustAnalyzerCmd::Ssr(cmd) => cli::apply_ssr_rules(cmd.rule)?, - flags::RustAnalyzerCmd::Search(cmd) => cli::search_for_patterns(cmd.pattern, cmd.debug)?, + flags::RustAnalyzerCmd::ProcMacro(flags::ProcMacro) => proc_macro_srv::cli::run()?, + flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?, + flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?, + flags::RustAnalyzerCmd::Highlight(cmd) => cmd.run()?, + flags::RustAnalyzerCmd::AnalysisStats(cmd) => cmd.run(verbosity)?, + flags::RustAnalyzerCmd::Diagnostics(cmd) => cmd.run()?, + flags::RustAnalyzerCmd::Ssr(cmd) => cmd.run()?, + flags::RustAnalyzerCmd::Search(cmd) => cmd.run()?, } Ok(()) } diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs index 00302616973..efd8a2aa9fc 100644 --- a/crates/rust-analyzer/src/cli.rs +++ b/crates/rust-analyzer/src/cli.rs @@ -2,24 +2,21 @@ pub mod flags; pub mod load_cargo; +mod parse; +mod symbols; +mod highlight; mod analysis_stats; mod diagnostics; -mod progress_report; mod ssr; +mod progress_report; + use std::io::Read; use anyhow::Result; -use ide::{Analysis, AnalysisHost}; -use syntax::{AstNode, SourceFile}; +use ide::AnalysisHost; use vfs::Vfs; -pub use self::{ - analysis_stats::AnalysisStatsCmd, - diagnostics::diagnostics, - ssr::{apply_ssr_rules, search_for_patterns}, -}; - #[derive(Clone, Copy)] pub enum Verbosity { Spammy, @@ -37,38 +34,6 @@ impl Verbosity { } } -pub fn parse(no_dump: bool) -> Result<()> { - let _p = profile::span("parsing"); - let file = file()?; - if !no_dump { - println!("{:#?}", file.syntax()); - } - std::mem::forget(file); - Ok(()) -} - -pub fn symbols() -> Result<()> { - let text = read_stdin()?; - let (analysis, file_id) = Analysis::from_single_file(text); - let structure = analysis.file_structure(file_id).unwrap(); - for s in structure { - println!("{:?}", s); - } - Ok(()) -} - -pub fn highlight(rainbow: bool) -> Result<()> { - let (analysis, file_id) = Analysis::from_single_file(read_stdin()?); - let html = analysis.highlight_as_html(file_id, rainbow).unwrap(); - println!("{}", html); - Ok(()) -} - -fn file() -> Result { - let text = read_stdin()?; - Ok(SourceFile::parse(&text).tree()) -} - fn read_stdin() -> Result { let mut buff = String::new(); std::io::stdin().read_to_string(&mut buff)?; diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 37da24f30e1..454510177e8 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -3,7 +3,6 @@ use std::{ env, - path::PathBuf, time::{SystemTime, UNIX_EPOCH}, }; @@ -28,6 +27,7 @@ use syntax::AstNode; use vfs::{Vfs, VfsPath}; use crate::cli::{ + flags, load_cargo::{load_workspace_at, LoadCargoConfig}, print_memory_usage, progress_report::ProgressReport, @@ -43,20 +43,7 @@ impl Clone for Snap> { } } -pub struct AnalysisStatsCmd { - pub randomize: bool, - pub parallel: bool, - pub memory_usage: bool, - pub only: Option, - pub with_deps: bool, - pub no_sysroot: bool, - pub path: PathBuf, - pub enable_build_scripts: bool, - pub enable_proc_macros: bool, - pub skip_inference: bool, -} - -impl AnalysisStatsCmd { +impl flags::AnalysisStats { pub fn run(self, verbosity: Verbosity) -> Result<()> { let mut rng = { let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u64; @@ -67,8 +54,8 @@ impl AnalysisStatsCmd { let mut cargo_config = CargoConfig::default(); cargo_config.no_sysroot = self.no_sysroot; let load_cargo_config = LoadCargoConfig { - load_out_dirs_from_check: self.enable_build_scripts, - with_proc_macro: self.enable_proc_macros, + load_out_dirs_from_check: !self.disable_build_scripts, + with_proc_macro: !self.disable_proc_macros, prefill_caches: false, }; let (host, vfs, _proc_macro) = diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index 84aaf1d5055..52511ceb580 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs @@ -1,9 +1,6 @@ -//! Analyze all modules in a project for diagnostics. Exits with a non-zero status -//! code if any errors are found. +//! Analyze all modules in a project for diagnostics. Exits with a non-zero +//! status code if any errors are found. -use std::path::Path; - -use anyhow::anyhow; use rustc_hash::FxHashSet; use hir::{db::HirDatabase, Crate, Module}; @@ -11,10 +8,70 @@ use ide::{AssistResolveStrategy, DiagnosticsConfig, Severity}; use ide_db::base_db::SourceDatabaseExt; use crate::cli::{ + flags, load_cargo::{load_workspace_at, LoadCargoConfig}, - Result, }; +impl flags::Diagnostics { + pub fn run(self) -> anyhow::Result<()> { + let cargo_config = Default::default(); + let load_cargo_config = LoadCargoConfig { + load_out_dirs_from_check: !self.disable_build_scripts, + with_proc_macro: !self.disable_proc_macros, + prefill_caches: false, + }; + let (host, _vfs, _proc_macro) = + load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; + let db = host.raw_database(); + let analysis = host.analysis(); + + let mut found_error = false; + let mut visited_files = FxHashSet::default(); + + let work = all_modules(db).into_iter().filter(|module| { + let file_id = module.definition_source(db).file_id.original_file(db); + let source_root = db.file_source_root(file_id); + let source_root = db.source_root(source_root); + !source_root.is_library + }); + + for module in work { + let file_id = module.definition_source(db).file_id.original_file(db); + if !visited_files.contains(&file_id) { + let crate_name = + module.krate().display_name(db).as_deref().unwrap_or("unknown").to_string(); + println!("processing crate: {}, module: {}", crate_name, _vfs.file_path(file_id)); + for diagnostic in analysis + .diagnostics( + &DiagnosticsConfig::default(), + AssistResolveStrategy::None, + file_id, + ) + .unwrap() + { + if matches!(diagnostic.severity, Severity::Error) { + found_error = true; + } + + println!("{:?}", diagnostic); + } + + visited_files.insert(file_id); + } + } + + println!(); + println!("diagnostic scan complete"); + + if found_error { + println!(); + anyhow::bail!("diagnostic error detected") + } + + Ok(()) + } +} + fn all_modules(db: &dyn HirDatabase) -> Vec { let mut worklist: Vec<_> = Crate::all(db).into_iter().map(|krate| krate.root_module(db)).collect(); @@ -27,58 +84,3 @@ fn all_modules(db: &dyn HirDatabase) -> Vec { modules } - -pub fn diagnostics( - path: &Path, - load_out_dirs_from_check: bool, - with_proc_macro: bool, -) -> Result<()> { - let cargo_config = Default::default(); - let load_cargo_config = - LoadCargoConfig { load_out_dirs_from_check, with_proc_macro, prefill_caches: false }; - let (host, _vfs, _proc_macro) = - load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; - let db = host.raw_database(); - let analysis = host.analysis(); - - let mut found_error = false; - let mut visited_files = FxHashSet::default(); - - let work = all_modules(db).into_iter().filter(|module| { - let file_id = module.definition_source(db).file_id.original_file(db); - let source_root = db.file_source_root(file_id); - let source_root = db.source_root(source_root); - !source_root.is_library - }); - - for module in work { - let file_id = module.definition_source(db).file_id.original_file(db); - if !visited_files.contains(&file_id) { - let crate_name = - module.krate().display_name(db).as_deref().unwrap_or("unknown").to_string(); - println!("processing crate: {}, module: {}", crate_name, _vfs.file_path(file_id)); - for diagnostic in analysis - .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::None, file_id) - .unwrap() - { - if matches!(diagnostic.severity, Severity::Error) { - found_error = true; - } - - println!("{:?}", diagnostic); - } - - visited_files.insert(file_id); - } - } - - println!(); - println!("diagnostic scan complete"); - - if found_error { - println!(); - Err(anyhow!("diagnostic error detected")) - } else { - Ok(()) - } -} diff --git a/crates/rust-analyzer/src/cli/highlight.rs b/crates/rust-analyzer/src/cli/highlight.rs new file mode 100644 index 00000000000..4f9b362f1be --- /dev/null +++ b/crates/rust-analyzer/src/cli/highlight.rs @@ -0,0 +1,14 @@ +//! Read Rust code on stdin, print HTML highlighted version to stdout. + +use ide::Analysis; + +use crate::cli::{flags, read_stdin}; + +impl flags::Highlight { + pub fn run(self) -> anyhow::Result<()> { + let (analysis, file_id) = Analysis::from_single_file(read_stdin()?); + let html = analysis.highlight_as_html(file_id, self.rainbow).unwrap(); + println!("{}", html); + Ok(()) + } +} diff --git a/crates/rust-analyzer/src/cli/parse.rs b/crates/rust-analyzer/src/cli/parse.rs new file mode 100644 index 00000000000..5ef8cdff4cf --- /dev/null +++ b/crates/rust-analyzer/src/cli/parse.rs @@ -0,0 +1,17 @@ +//! Read Rust code on stdin, print syntax tree on stdout. +use syntax::{AstNode, SourceFile}; + +use crate::cli::{flags, read_stdin}; + +impl flags::Parse { + pub fn run(self) -> anyhow::Result<()> { + let _p = profile::span("parsing"); + let text = read_stdin()?; + let file = SourceFile::parse(&text).tree(); + if !self.no_dump { + println!("{:#?}", file.syntax()); + } + std::mem::forget(file); + Ok(()) + } +} diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs index 60092127d96..e8291782b7a 100644 --- a/crates/rust-analyzer/src/cli/ssr.rs +++ b/crates/rust-analyzer/src/cli/ssr.rs @@ -1,72 +1,86 @@ //! Applies structured search replace rules from the command line. +use ide_ssr::MatchFinder; +use project_model::CargoConfig; + use crate::cli::{ + flags, load_cargo::{load_workspace_at, LoadCargoConfig}, Result, }; -use ide_ssr::{MatchFinder, SsrPattern, SsrRule}; -use project_model::CargoConfig; -pub fn apply_ssr_rules(rules: Vec) -> Result<()> { - use ide_db::base_db::SourceDatabaseExt; - let cargo_config = CargoConfig::default(); - let load_cargo_config = LoadCargoConfig { - load_out_dirs_from_check: true, - with_proc_macro: true, - prefill_caches: false, - }; - let (host, vfs, _proc_macro) = - load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; - let db = host.raw_database(); - let mut match_finder = MatchFinder::at_first_file(db)?; - for rule in rules { - match_finder.add_rule(rule)?; - } - let edits = match_finder.edits(); - for (file_id, edit) in edits { - if let Some(path) = vfs.file_path(file_id).as_path() { - let mut contents = db.file_text(file_id).to_string(); - edit.apply(&mut contents); - std::fs::write(path, contents)?; +impl flags::Ssr { + pub fn run(self) -> Result<()> { + use ide_db::base_db::SourceDatabaseExt; + let cargo_config = CargoConfig::default(); + let load_cargo_config = LoadCargoConfig { + load_out_dirs_from_check: true, + with_proc_macro: true, + prefill_caches: false, + }; + let (host, vfs, _proc_macro) = load_workspace_at( + &std::env::current_dir()?, + &cargo_config, + &load_cargo_config, + &|_| {}, + )?; + let db = host.raw_database(); + let mut match_finder = MatchFinder::at_first_file(db)?; + for rule in self.rule { + match_finder.add_rule(rule)?; } - } - Ok(()) -} - -/// Searches for `patterns`, printing debug information for any nodes whose text exactly matches -/// `debug_snippet`. This is intended for debugging and probably isn't in it's current form useful -/// for much else. -pub fn search_for_patterns(patterns: Vec, debug_snippet: Option) -> Result<()> { - use ide_db::base_db::SourceDatabaseExt; - use ide_db::symbol_index::SymbolsDatabase; - let cargo_config = CargoConfig::default(); - let load_cargo_config = LoadCargoConfig { - load_out_dirs_from_check: true, - with_proc_macro: true, - prefill_caches: false, - }; - let (host, _vfs, _proc_macro) = - load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; - let db = host.raw_database(); - let mut match_finder = MatchFinder::at_first_file(db)?; - for pattern in patterns { - match_finder.add_search_pattern(pattern)?; - } - if let Some(debug_snippet) = &debug_snippet { - for &root in db.local_roots().iter() { - let sr = db.source_root(root); - for file_id in sr.iter() { - for debug_info in match_finder.debug_where_text_equal(file_id, debug_snippet) { - println!("{:#?}", debug_info); - } + let edits = match_finder.edits(); + for (file_id, edit) in edits { + if let Some(path) = vfs.file_path(file_id).as_path() { + let mut contents = db.file_text(file_id).to_string(); + edit.apply(&mut contents); + std::fs::write(path, contents)?; } } - } else { - for m in match_finder.matches().flattened().matches { - // We could possibly at some point do something more useful than just printing - // the matched text. For now though, that's the easiest thing to do. - println!("{}", m.matched_text()); - } + Ok(()) + } +} + +impl flags::Search { + /// Searches for `patterns`, printing debug information for any nodes whose text exactly matches + /// `debug_snippet`. This is intended for debugging and probably isn't in it's current form useful + /// for much else. + pub fn run(self) -> Result<()> { + use ide_db::base_db::SourceDatabaseExt; + use ide_db::symbol_index::SymbolsDatabase; + let cargo_config = CargoConfig::default(); + let load_cargo_config = LoadCargoConfig { + load_out_dirs_from_check: true, + with_proc_macro: true, + prefill_caches: false, + }; + let (host, _vfs, _proc_macro) = load_workspace_at( + &std::env::current_dir()?, + &cargo_config, + &load_cargo_config, + &|_| {}, + )?; + let db = host.raw_database(); + let mut match_finder = MatchFinder::at_first_file(db)?; + for pattern in self.pattern { + match_finder.add_search_pattern(pattern)?; + } + if let Some(debug_snippet) = &self.debug { + for &root in db.local_roots().iter() { + let sr = db.source_root(root); + for file_id in sr.iter() { + for debug_info in match_finder.debug_where_text_equal(file_id, debug_snippet) { + println!("{:#?}", debug_info); + } + } + } + } else { + for m in match_finder.matches().flattened().matches { + // We could possibly at some point do something more useful than just printing + // the matched text. For now though, that's the easiest thing to do. + println!("{}", m.matched_text()); + } + } + Ok(()) } - Ok(()) } diff --git a/crates/rust-analyzer/src/cli/symbols.rs b/crates/rust-analyzer/src/cli/symbols.rs new file mode 100644 index 00000000000..84659b5ea9c --- /dev/null +++ b/crates/rust-analyzer/src/cli/symbols.rs @@ -0,0 +1,16 @@ +//! Read Rust code on stdin, print syntax tree on stdout. +use ide::Analysis; + +use crate::cli::{flags, read_stdin}; + +impl flags::Symbols { + pub fn run(self) -> anyhow::Result<()> { + let text = read_stdin()?; + let (analysis, file_id) = Analysis::from_single_file(text); + let structure = analysis.file_structure(file_id).unwrap(); + for s in structure { + println!("{:?}", s); + } + Ok(()) + } +}