mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-16 01:38:13 +00:00
internal: unify subcommand handling between ra and xtask
This commit is contained in:
parent
e8a67b67bf
commit
9cf5914c45
8 changed files with 203 additions and 209 deletions
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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<SourceFile> {
|
||||
let text = read_stdin()?;
|
||||
Ok(SourceFile::parse(&text).tree())
|
||||
}
|
||||
|
||||
fn read_stdin() -> Result<String> {
|
||||
let mut buff = String::new();
|
||||
std::io::stdin().read_to_string(&mut buff)?;
|
||||
|
|
|
@ -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<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct AnalysisStatsCmd {
|
||||
pub randomize: bool,
|
||||
pub parallel: bool,
|
||||
pub memory_usage: bool,
|
||||
pub only: Option<String>,
|
||||
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) =
|
||||
|
|
|
@ -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<Module> {
|
||||
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<Module> {
|
|||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
14
crates/rust-analyzer/src/cli/highlight.rs
Normal file
14
crates/rust-analyzer/src/cli/highlight.rs
Normal file
|
@ -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(())
|
||||
}
|
||||
}
|
17
crates/rust-analyzer/src/cli/parse.rs
Normal file
17
crates/rust-analyzer/src/cli/parse.rs
Normal file
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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<SsrRule>) -> 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<SsrPattern>, debug_snippet: Option<String>) -> 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(())
|
||||
}
|
||||
|
|
16
crates/rust-analyzer/src/cli/symbols.rs
Normal file
16
crates/rust-analyzer/src/cli/symbols.rs
Normal file
|
@ -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(())
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue