From 3038579c8ed446b296ff8afbe51ffcfa9c1869a2 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Mar 2021 15:01:48 +0300 Subject: Switch from pico-args to xflags --- crates/rust-analyzer/Cargo.toml | 2 +- crates/rust-analyzer/src/bin/args.rs | 274 ------------------------- crates/rust-analyzer/src/bin/flags.rs | 251 ++++++++++++++++++++++ crates/rust-analyzer/src/bin/main.rs | 85 +++++--- crates/rust-analyzer/src/cli/analysis_bench.rs | 1 + 5 files changed, 312 insertions(+), 301 deletions(-) delete mode 100644 crates/rust-analyzer/src/bin/args.rs create mode 100644 crates/rust-analyzer/src/bin/flags.rs (limited to 'crates') diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index b881cc229..8789f0852 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -24,7 +24,7 @@ jod-thread = "0.1.0" log = "0.4.8" lsp-types = { version = "0.88.0", features = ["proposed"] } parking_lot = "0.11.0" -pico-args = "0.4.0" +xflags = "0.1.2" oorandom = "11.1.2" rustc-hash = "1.1.0" serde = { version = "1.0.106", features = ["derive"] } diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs deleted file mode 100644 index 164d94a30..000000000 --- a/crates/rust-analyzer/src/bin/args.rs +++ /dev/null @@ -1,274 +0,0 @@ -//! Command like parsing for rust-analyzer. -//! -//! If run started args, we run the LSP server loop. With a subcommand, we do a -//! one-time batch processing. - -use std::{env, path::PathBuf}; - -use anyhow::{bail, format_err, Result}; -use ide_ssr::{SsrPattern, SsrRule}; -use pico_args::Arguments; -use rust_analyzer::cli::{AnalysisStatsCmd, BenchCmd, BenchWhat, Position, Verbosity}; -use vfs::AbsPathBuf; - -pub(crate) struct Args { - pub(crate) verbosity: Verbosity, - pub(crate) log_file: Option, - pub(crate) no_buffering: bool, - pub(crate) command: Command, - #[allow(unused)] - pub(crate) wait_dbg: bool, -} - -pub(crate) enum Command { - Parse { no_dump: bool }, - Symbols, - Highlight { rainbow: bool }, - AnalysisStats(AnalysisStatsCmd), - Bench(BenchCmd), - Diagnostics { path: PathBuf, load_output_dirs: bool, with_proc_macro: bool }, - Ssr { rules: Vec }, - StructuredSearch { debug_snippet: Option, patterns: Vec }, - ProcMacro, - RunServer, - PrintConfigSchema, - Version, - Help, -} - -const HELP: &str = "\ -rust-analyzer - -USAGE: - rust-analyzer [FLAGS] [COMMAND] [COMMAND_OPTIONS] - -FLAGS: - --version Print version - -h, --help Print this help - - -v, --verbose - -vv, --spammy - -q, --quiet Set verbosity - - --print-config-schema - Dump a LSP config JSON schema - --log-file Log to the specified file instead of stderr - --no-log-buffering - Flush log records to the file immediately - - --wait-dbg Wait until a debugger is attached to. - The flag is valid for debug builds only - -ENVIRONMENTAL VARIABLES: - RA_LOG Set log filter in env_logger format - RA_PROFILE Enable hierarchical profiler - RA_WAIT_DBG If set acts like a --wait-dbg flag - -COMMANDS: - -not specified Launch LSP server - -parse < main.rs Parse tree - --no-dump Suppress printing - -symbols < main.rs Parse input an print the list of symbols - -highlight < main.rs Highlight input as html - --rainbow Enable rainbow highlighting of identifiers - -analysis-stats Batch typecheck project and print summary statistics - Directory with Cargo.toml - --randomize Randomize order in which crates, modules, and items are processed - --parallel Run type inference in parallel - --memory-usage Collect memory usage statistics - -o, --only Only analyze items matching this path - --with-deps Also analyze all dependencies - --load-output-dirs - Load OUT_DIR values by running `cargo check` before analysis - --with-proc-macro Use proc-macro-srv for proc-macro expanding - -analysis-bench Benchmark specific analysis operation - Directory with Cargo.toml - --highlight - Compute syntax highlighting for this file - --complete - Compute completions at this location - --goto-def - Compute goto definition at this location - --memory-usage Collect memory usage statistics - --load-output-dirs - Load OUT_DIR values by running `cargo check` before analysis - --with-proc-macro Use proc-macro-srv for proc-macro expanding - -diagnostics - Directory with Cargo.toml - --load-output-dirs - Load OUT_DIR values by running `cargo check` before analysis - --with-proc-macro Use proc-macro-srv for proc-macro expanding - -ssr [RULE...] - A structured search replace rule (`$a.foo($b) ==> bar($a, $b)`) - -search [PATTERN..] - A structured search replace pattern (`$a.foo($b)`) - --debug Prints debug information for any nodes with source exactly - equal to -"; - -impl Args { - pub(crate) fn parse() -> Result { - let mut matches = Arguments::from_env(); - - if matches.contains("--version") { - finish_args(matches)?; - return Ok(Args { - verbosity: Verbosity::Normal, - log_file: None, - command: Command::Version, - no_buffering: false, - wait_dbg: false, - }); - } - - let verbosity = match ( - matches.contains(["-vv", "--spammy"]), - matches.contains(["-v", "--verbose"]), - matches.contains(["-q", "--quiet"]), - ) { - (true, _, true) => bail!("Invalid flags: -q conflicts with -vv"), - (true, _, false) => Verbosity::Spammy, - (false, false, false) => Verbosity::Normal, - (false, false, true) => Verbosity::Quiet, - (false, true, false) => Verbosity::Verbose, - (false, true, true) => bail!("Invalid flags: -q conflicts with -v"), - }; - let log_file = matches.opt_value_from_str("--log-file")?; - let no_buffering = matches.contains("--no-log-buffering"); - let wait_dbg = matches.contains("--wait-dbg"); - - if matches.contains(["-h", "--help"]) { - eprintln!("{}", HELP); - return Ok(Args { - verbosity, - log_file: None, - command: Command::Help, - no_buffering, - wait_dbg, - }); - } - - if matches.contains("--print-config-schema") { - return Ok(Args { - verbosity, - log_file, - command: Command::PrintConfigSchema, - no_buffering, - wait_dbg, - }); - } - - let subcommand = match matches.subcommand()? { - Some(it) => it, - None => { - finish_args(matches)?; - return Ok(Args { - verbosity, - log_file, - command: Command::RunServer, - no_buffering, - wait_dbg, - }); - } - }; - let command = match subcommand.as_str() { - "parse" => Command::Parse { no_dump: matches.contains("--no-dump") }, - "symbols" => Command::Symbols, - "highlight" => Command::Highlight { rainbow: matches.contains("--rainbow") }, - "analysis-stats" => Command::AnalysisStats(AnalysisStatsCmd { - randomize: matches.contains("--randomize"), - parallel: matches.contains("--parallel"), - memory_usage: matches.contains("--memory-usage"), - only: matches.opt_value_from_str(["-o", "--only"])?, - with_deps: matches.contains("--with-deps"), - load_output_dirs: matches.contains("--load-output-dirs"), - with_proc_macro: matches.contains("--with-proc-macro"), - path: matches - .opt_free_from_str()? - .ok_or_else(|| format_err!("expected positional argument"))?, - }), - "analysis-bench" => Command::Bench(BenchCmd { - what: { - let highlight_path: Option = - matches.opt_value_from_str("--highlight")?; - let complete_path: Option = - matches.opt_value_from_str("--complete")?; - let goto_def_path: Option = - matches.opt_value_from_str("--goto-def")?; - match (highlight_path, complete_path, goto_def_path) { - (Some(path), None, None) => { - let path = env::current_dir().unwrap().join(path); - BenchWhat::Highlight { path: AbsPathBuf::assert(path) } - } - (None, Some(position), None) => BenchWhat::Complete(position), - (None, None, Some(position)) => BenchWhat::GotoDef(position), - _ => panic!( - "exactly one of `--highlight`, `--complete` or `--goto-def` must be set" - ), - } - }, - memory_usage: matches.contains("--memory-usage"), - load_output_dirs: matches.contains("--load-output-dirs"), - with_proc_macro: matches.contains("--with-proc-macro"), - path: matches - .opt_free_from_str()? - .ok_or_else(|| format_err!("expected positional argument"))?, - }), - "diagnostics" => Command::Diagnostics { - load_output_dirs: matches.contains("--load-output-dirs"), - with_proc_macro: matches.contains("--with-proc-macro"), - path: matches - .opt_free_from_str()? - .ok_or_else(|| format_err!("expected positional argument"))?, - }, - "proc-macro" => Command::ProcMacro, - "ssr" => Command::Ssr { - rules: { - let mut acc = Vec::new(); - while let Some(rule) = matches.opt_free_from_str()? { - acc.push(rule); - } - acc - }, - }, - "search" => Command::StructuredSearch { - debug_snippet: matches.opt_value_from_str("--debug")?, - patterns: { - let mut acc = Vec::new(); - while let Some(rule) = matches.opt_free_from_str()? { - acc.push(rule); - } - acc - }, - }, - _ => { - eprintln!("{}", HELP); - return Ok(Args { - verbosity, - log_file: None, - command: Command::Help, - no_buffering, - wait_dbg, - }); - } - }; - finish_args(matches)?; - Ok(Args { verbosity, log_file, command, no_buffering, wait_dbg }) - } -} - -fn finish_args(args: Arguments) -> Result<()> { - if !args.finish().is_empty() { - bail!("Unused arguments."); - } - Ok(()) -} diff --git a/crates/rust-analyzer/src/bin/flags.rs b/crates/rust-analyzer/src/bin/flags.rs new file mode 100644 index 000000000..244912d26 --- /dev/null +++ b/crates/rust-analyzer/src/bin/flags.rs @@ -0,0 +1,251 @@ +//! Grammar for the command-line arguments. +#![allow(unreachable_pub)] +use std::{env, path::PathBuf}; + +use ide_ssr::{SsrPattern, SsrRule}; +use rust_analyzer::cli::{BenchWhat, Position, Verbosity}; +use vfs::AbsPathBuf; + +xflags::args_parser! { + /// LSP server for the Rust programming language. + cmd rust-analyzer { + /// Verbosity level, can be repeated multiple times. + repeated -v, --verbose + /// Verbosity level. + optional -q, --quiet + + /// Log to the specified file instead of stderr. + optional --log-file path: PathBuf + /// Flush log records to the file immediately. + optional --no-log-buffering + + /// Wait until a debugger is attached to (requires debug build). + optional --wait-dbg + + default cmd lsp-server { + /// Print version. + optional --version + /// Print help. + optional -h, --help + + /// Dump a LSP config JSON schema. + optional --print-config-schema + } + + /// Parse stdin. + cmd parse { + /// Suppress printing. + optional --no-dump + } + + /// Parse stdin and print the list of symbols. + cmd symbols {} + + /// Highlight stdin as html. + cmd highlight { + /// Enable rainbow highlighting of identifiers. + optional --rainbow + } + + /// Batch typecheck project and print summary statistics + cmd analysis-stats + /// Directory with Cargo.toml. + required path: PathBuf + { + /// Randomize order in which crates, modules, and items are processed. + optional --randomize + /// Run type inference in parallel. + optional --parallel + /// Collect memory usage statistics. + optional --memory-usage + + /// Only analyze items matching this path. + optional -o, --only path: String + /// Also analyze all dependencies. + optional --with-deps + + /// Load OUT_DIR values by running `cargo check` before analysis. + optional --load-output-dirs + /// Use proc-macro-srv for proc-macro expanding. + optional --with-proc-macro + } + + /// Benchmark specific analysis operation + cmd analysis-bench + /// Directory with Cargo.toml. + required path: PathBuf + { + /// Collect memory usage statistics. + optional --memory-usage + + /// Compute syntax highlighting for this file + optional --highlight path: PathBuf + /// Compute completions at file:line:column location. + optional --complete location: Position + /// Compute goto definition at file:line:column location. + optional --goto-def location: Position + + /// Load OUT_DIR values by running `cargo check` before analysis. + optional --load-output-dirs + /// Use proc-macro-srv for proc-macro expanding. + optional --with-proc-macro + } + + cmd diagnostics + /// Directory with Cargo.toml. + required path: PathBuf + { + /// Load OUT_DIR values by running `cargo check` before analysis. + optional --load-output-dirs + /// Use proc-macro-srv for proc-macro expanding. + optional --with-proc-macro + } + + cmd ssr + /// A structured search replace rule (`$a.foo($b) ==> bar($a, $b)`) + repeated rule: SsrRule + {} + + cmd search + /// A structured search replace pattern (`$a.foo($b)`) + repeated pattern: SsrPattern + { + /// Prints debug information for any nodes with source exactly equal to snippet. + optional --debug snippet: String + } + + cmd proc-macro {} + } +} + +// generated start +// The following code is generated by `xflags` macro. +// Run `env XFLAGS_DUMP= cargo build` to regenerate. +#[derive(Debug)] +pub struct RustAnalyzer { + pub verbose: u32, + pub quiet: bool, + pub log_file: Option, + pub no_log_buffering: bool, + pub wait_dbg: bool, + pub subcommand: RustAnalyzerCmd, +} + +#[derive(Debug)] +pub enum RustAnalyzerCmd { + LspServer(LspServer), + Parse(Parse), + Symbols(Symbols), + Highlight(Highlight), + AnalysisStats(AnalysisStats), + AnalysisBench(AnalysisBench), + Diagnostics(Diagnostics), + Ssr(Ssr), + Search(Search), + ProcMacro(ProcMacro), +} + +#[derive(Debug)] +pub struct LspServer { + pub version: bool, + pub help: bool, + pub print_config_schema: bool, +} + +#[derive(Debug)] +pub struct Parse { + pub no_dump: bool, +} + +#[derive(Debug)] +pub struct Symbols {} + +#[derive(Debug)] +pub struct Highlight { + pub rainbow: bool, +} + +#[derive(Debug)] +pub struct AnalysisStats { + pub path: PathBuf, + + pub randomize: bool, + pub parallel: bool, + pub memory_usage: bool, + pub only: Option, + pub with_deps: bool, + pub load_output_dirs: bool, + pub with_proc_macro: bool, +} + +#[derive(Debug)] +pub struct AnalysisBench { + pub path: PathBuf, + + pub memory_usage: bool, + pub highlight: Option, + pub complete: Option, + pub goto_def: Option, + pub load_output_dirs: bool, + pub with_proc_macro: bool, +} + +#[derive(Debug)] +pub struct Diagnostics { + pub path: PathBuf, + + pub load_output_dirs: bool, + pub with_proc_macro: bool, +} + +#[derive(Debug)] +pub struct Ssr { + pub rule: Vec, +} + +#[derive(Debug)] +pub struct Search { + pub pattern: Vec, + + pub debug: Option, +} + +#[derive(Debug)] +pub struct ProcMacro {} + +impl RustAnalyzer { + pub const HELP: &'static str = Self::_HELP; + + pub fn from_env() -> xflags::Result { + let mut p = xflags::rt::Parser::new_from_env(); + Self::_parse(&mut p) + } +} +// generated end + +impl RustAnalyzer { + pub(crate) fn verbosity(&self) -> Verbosity { + if self.quiet { + return Verbosity::Quiet; + } + match self.verbose { + 0 => Verbosity::Normal, + 1 => Verbosity::Verbose, + _ => Verbosity::Spammy, + } + } +} + +impl AnalysisBench { + pub(crate) fn what(&self) -> BenchWhat { + match (&self.highlight, &self.complete, &self.goto_def) { + (Some(path), None, None) => { + let path = env::current_dir().unwrap().join(path); + BenchWhat::Highlight { path: AbsPathBuf::assert(path) } + } + (None, Some(position), None) => BenchWhat::Complete(position.clone()), + (None, None, Some(position)) => BenchWhat::GotoDef(position.clone()), + _ => panic!("exactly one of `--highlight`, `--complete` or `--goto-def` must be set"), + } + } +} diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 89482b952..288847980 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -1,14 +1,20 @@ //! Driver for rust-analyzer. //! //! Based on cli flags, either spawns an LSP server, or runs a batch analysis -mod args; +mod flags; mod logger; -use std::{convert::TryFrom, env, fs, path::PathBuf, process}; +use std::{convert::TryFrom, env, fs, path::Path, process}; use lsp_server::Connection; use project_model::ProjectManifest; -use rust_analyzer::{cli, config::Config, from_json, lsp_ext::supports_utf8, Result}; +use rust_analyzer::{ + cli::{self, AnalysisStatsCmd, BenchCmd}, + config::Config, + from_json, + lsp_ext::supports_utf8, + Result, +}; use vfs::AbsPathBuf; #[cfg(all(feature = "mimalloc"))] @@ -28,10 +34,10 @@ fn main() { } fn try_main() -> Result<()> { - let args = args::Args::parse()?; + let flags = flags::RustAnalyzer::from_env()?; #[cfg(debug_assertions)] - if args.wait_dbg || env::var("RA_WAIT_DBG").is_ok() { + if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() { #[allow(unused_mut)] let mut d = 4; while d == 4 { @@ -39,35 +45,62 @@ fn try_main() -> Result<()> { } } - setup_logging(args.log_file, args.no_buffering)?; - match args.command { - args::Command::RunServer => run_server()?, - args::Command::PrintConfigSchema => { - println!("{:#}", Config::json_schema()); + setup_logging(flags.log_file.as_deref(), flags.no_log_buffering)?; + let verbosity = flags.verbosity(); + + match flags.subcommand { + flags::RustAnalyzerCmd::LspServer(cmd) => { + if cmd.print_config_schema { + println!("{:#}", Config::json_schema()); + return Ok(()); + } + if cmd.version { + println!("rust-analyzer {}", env!("REV")); + return Ok(()); + } + if cmd.help { + println!("{}", flags::RustAnalyzer::HELP); + return Ok(()); + } + run_server()? } - args::Command::ProcMacro => proc_macro_srv::cli::run()?, - - args::Command::Parse { no_dump } => cli::parse(no_dump)?, - args::Command::Symbols => cli::symbols()?, - args::Command::Highlight { rainbow } => cli::highlight(rainbow)?, - args::Command::AnalysisStats(cmd) => cmd.run(args.verbosity)?, - args::Command::Bench(cmd) => cmd.run(args.verbosity)?, - args::Command::Diagnostics { path, load_output_dirs, with_proc_macro } => { - cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro)? + 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, + path: cmd.path, + load_output_dirs: cmd.load_output_dirs, + with_proc_macro: cmd.with_proc_macro, } - args::Command::Ssr { rules } => { - cli::apply_ssr_rules(rules)?; + .run(verbosity)?, + flags::RustAnalyzerCmd::AnalysisBench(cmd) => { + let what = cmd.what(); + BenchCmd { + memory_usage: cmd.memory_usage, + path: cmd.path, + load_output_dirs: cmd.load_output_dirs, + with_proc_macro: cmd.with_proc_macro, + what, + } + .run(verbosity)? } - args::Command::StructuredSearch { patterns, debug_snippet } => { - cli::search_for_patterns(patterns, debug_snippet)?; + + flags::RustAnalyzerCmd::Diagnostics(cmd) => { + cli::diagnostics(&cmd.path, cmd.load_output_dirs, cmd.with_proc_macro)? } - args::Command::Version => println!("rust-analyzer {}", env!("REV")), - args::Command::Help => {} + flags::RustAnalyzerCmd::Ssr(cmd) => cli::apply_ssr_rules(cmd.rule)?, + flags::RustAnalyzerCmd::Search(cmd) => cli::search_for_patterns(cmd.pattern, cmd.debug)?, } Ok(()) } -fn setup_logging(log_file: Option, no_buffering: bool) -> Result<()> { +fn setup_logging(log_file: Option<&Path>, no_buffering: bool) -> Result<()> { env::set_var("RUST_BACKTRACE", "short"); let log_file = match log_file { diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs index ebbbf0596..3bd7e678d 100644 --- a/crates/rust-analyzer/src/cli/analysis_bench.rs +++ b/crates/rust-analyzer/src/cli/analysis_bench.rs @@ -35,6 +35,7 @@ pub enum BenchWhat { GotoDef(Position), } +#[derive(Debug, Clone)] pub struct Position { pub path: AbsPathBuf, pub line: u32, -- cgit v1.2.3