internal: unify subcommand handling between ra and xtask

This commit is contained in:
Aleksey Kladov 2021-08-10 12:49:55 +03:00
parent e8a67b67bf
commit 9cf5914c45
8 changed files with 203 additions and 209 deletions

View file

@ -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(())
}

View file

@ -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)?;

View file

@ -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) =

View file

@ -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(())
}
}

View 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(())
}
}

View 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(())
}
}

View file

@ -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(())
}

View 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(())
}
}