diff --git a/src/args.rs b/src/args.rs index 4a3398a..503e37c 100644 --- a/src/args.rs +++ b/src/args.rs @@ -41,9 +41,10 @@ impl Default for Format { setting = structopt::clap::AppSettings::DeriveDisplayOrder, setting = structopt::clap::AppSettings::DontCollapseArgsInUsage )] +#[structopt(group = structopt::clap::ArgGroup::with_name("mode").multiple(false))] pub(crate) struct Args { #[structopt(parse(from_os_str), default_value = ".")] - /// Paths to check + /// Paths to check with `-` for stdin pub(crate) path: Vec, #[structopt(short = "c", long = "config")] @@ -54,26 +55,30 @@ pub(crate) struct Args { /// Ignore implicit configuration files. pub(crate) isolated: bool, - #[structopt(long)] + #[structopt(long, group = "mode")] /// Print a diff of what would change pub(crate) diff: bool, - #[structopt(long, short = "w")] - /// Write corrections out + #[structopt(long, short = "w", group = "mode")] + /// Write fixes out pub(crate) write_changes: bool, - #[structopt(long)] - /// Print each file that would be spellchecked. + #[structopt(long, group = "mode")] + /// Debug: Print each file that would be spellchecked. pub(crate) files: bool, - #[structopt(long)] - /// Print each identifier that would be spellchecked. + #[structopt(long, group = "mode")] + /// Debug: Print each identifier that would be spellchecked. pub(crate) identifiers: bool, - #[structopt(long)] - /// Print each word that would be spellchecked. + #[structopt(long, group = "mode")] + /// Debug: Print each word that would be spellchecked. pub(crate) words: bool, + #[structopt(long, group = "mode")] + /// Write the current configuration to file with `-` for stdout + pub(crate) dump_config: Option, + #[structopt(flatten)] pub(crate) overrides: FileArgs, diff --git a/src/config.rs b/src/config.rs index 5f4e613..a9b8cbc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -118,6 +118,13 @@ impl Config { Ok(content) } + pub fn from_defaults() -> Self { + Self { + files: Walk::from_defaults(), + default: FileConfig::from_defaults(), + } + } + pub fn derive(cwd: &std::path::Path) -> Result { if let Some(path) = find_project_file(cwd, &["typos.toml", "_typos.toml", ".typos.toml"]) { Self::from_file(&path) @@ -160,6 +167,19 @@ pub struct Walk { } impl Walk { + pub fn from_defaults() -> Self { + let empty = Self::default(); + Self { + binary: Some(empty.binary()), + ignore_hidden: Some(empty.ignore_hidden()), + ignore_files: Some(true), + ignore_dot: Some(empty.ignore_dot()), + ignore_vcs: Some(empty.ignore_vcs()), + ignore_global: Some(empty.ignore_global()), + ignore_parent: Some(empty.ignore_parent()), + } + } + pub fn update(&mut self, source: &dyn WalkSource) { if let Some(source) = source.binary() { self.binary = Some(source); @@ -264,6 +284,22 @@ pub struct FileConfig { } impl FileConfig { + pub fn from_defaults() -> Self { + let empty = Self::default(); + FileConfig { + check_filename: Some(empty.check_filename()), + check_file: Some(empty.check_file()), + ignore_hex: Some(empty.ignore_hex()), + identifier_leading_digits: Some(empty.identifier_leading_digits()), + identifier_leading_chars: Some(empty.identifier_leading_chars().to_owned()), + identifier_include_digits: Some(empty.identifier_include_digits()), + identifier_include_chars: Some(empty.identifier_include_chars().to_owned()), + locale: Some(empty.locale()), + extend_identifiers: Default::default(), + extend_words: Default::default(), + } + } + pub fn update(&mut self, source: &dyn FileSource) { if let Some(source) = source.check_filename() { self.check_filename = Some(source); diff --git a/src/main.rs b/src/main.rs index a5a5d89..b8095b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,13 +34,46 @@ fn run() -> proc_exit::ExitResult { }; init_logging(args.verbose.log_level()); + + if let Some(output_path) = args.dump_config.as_ref() { + run_dump_config(&args, output_path) + } else { + run_checks(&args) + } +} + +fn run_dump_config(args: &args::Args, output_path: &std::path::Path) -> proc_exit::ExitResult { let global_cwd = std::env::current_dir()?; - let config = if let Some(path) = args.custom_config.as_ref() { - config::Config::from_file(path).with_code(proc_exit::Code::CONFIG_ERR)? + let path = &args.path[0]; + let path = if path == std::path::Path::new("-") { + path.to_owned() } else { - config::Config::default() + path.canonicalize().with_code(proc_exit::Code::USAGE_ERR)? }; + let cwd = if path == std::path::Path::new("-") { + global_cwd.as_path() + } else if path.is_file() { + path.parent().unwrap() + } else { + path.as_path() + }; + + let config = load_config(cwd, &args).with_code(proc_exit::Code::CONFIG_ERR)?; + let mut defaulted_config = config::Config::from_defaults(); + defaulted_config.update(&config); + let output = toml::to_string_pretty(&defaulted_config).with_code(proc_exit::Code::FAILURE)?; + if output_path == std::path::Path::new("-") { + std::io::stdout().write_all(output.as_bytes())?; + } else { + std::fs::write(output_path, &output)?; + } + + Ok(()) +} + +fn run_checks(args: &args::Args) -> proc_exit::ExitResult { + let global_cwd = std::env::current_dir()?; let mut typos_found = false; let mut errors_found = false; @@ -57,15 +90,7 @@ fn run() -> proc_exit::ExitResult { } else { path.as_path() }; - - let mut config = config.clone(); - if !args.isolated { - let derived = config::Config::derive(cwd).with_code(proc_exit::Code::CONFIG_ERR)?; - config.update(&derived); - } - config.update(&args.config); - config.default.update(&args.overrides); - let config = config; + let config = load_config(cwd, &args).with_code(proc_exit::Code::CONFIG_ERR)?; let parser = typos::tokens::TokenizerBuilder::new() .ignore_hex(config.default.ignore_hex()) @@ -194,3 +219,20 @@ fn init_logging(level: Option) { builder.init(); } } + +fn load_config(cwd: &std::path::Path, args: &args::Args) -> Result { + let mut config = config::Config::default(); + + if !args.isolated { + let derived = config::Config::derive(cwd)?; + config.update(&derived); + } + if let Some(path) = args.custom_config.as_ref() { + config.update(&config::Config::from_file(path)?); + } + + config.update(&args.config); + config.default.update(&args.overrides); + + Ok(config) +}