diff --git a/crates/typos/src/checks.rs b/crates/typos/src/checks.rs index 984413f..9a2404d 100644 --- a/crates/typos/src/checks.rs +++ b/crates/typos/src/checks.rs @@ -112,19 +112,12 @@ impl ParseIdentifiers { return Ok(typos_found); } - let buffer = std::fs::read(path) - .map_err(|e| crate::ErrorKind::IoError.into_error().with_source(e))?; - if !explicit && !self.binary { - let content_type = content_inspector::inspect(&buffer); - if content_type.is_binary() - // HACK: We only support UTF-8 at the moment - || (content_type != content_inspector::ContentType::UTF_8_BOM - && content_type != content_inspector::ContentType::UTF_8) - { - let msg = report::BinaryFile { path }; - reporter.report(msg.into()); - return Ok(typos_found); - } + let buffer = read_file(path)?; + let (buffer, content_type) = massage_data(buffer)?; + if !explicit && !self.binary && content_type.is_binary() { + let msg = report::BinaryFile { path }; + reporter.report(msg.into()); + return Ok(typos_found); } for line in buffer.lines() { @@ -188,19 +181,12 @@ impl ParseWords { return Ok(typos_found); } - let buffer = std::fs::read(path) - .map_err(|e| crate::ErrorKind::IoError.into_error().with_source(e))?; - if !explicit && !self.binary { - let content_type = content_inspector::inspect(&buffer); - // HACK: We only support UTF-8 at the moment - if content_type.is_binary() - || (content_type != content_inspector::ContentType::UTF_8_BOM - && content_type != content_inspector::ContentType::UTF_8) - { - let msg = report::BinaryFile { path }; - reporter.report(msg.into()); - return Ok(typos_found); - } + let buffer = read_file(path)?; + let (buffer, content_type) = massage_data(buffer)?; + if !explicit && !self.binary && content_type.is_binary() { + let msg = report::BinaryFile { path }; + reporter.report(msg.into()); + return Ok(typos_found); } for line in buffer.lines() { @@ -249,7 +235,7 @@ impl Checks { Some(Status::Valid) => {} Some(corrections) => { let byte_offset = ident.offset(); - let msg = report::PathCorrection { + let msg = report::PathTypo { path, byte_offset, typo: ident.token(), @@ -263,7 +249,7 @@ impl Checks { Some(Status::Valid) => {} Some(corrections) => { let byte_offset = word.offset(); - let msg = report::PathCorrection { + let msg = report::PathTypo { path, byte_offset, typo: word.token(), @@ -295,20 +281,12 @@ impl Checks { return Ok(typos_found); } - let buffer = std::fs::read(path) - .map_err(|e| crate::ErrorKind::IoError.into_error().with_source(e))?; - if !explicit && !self.binary { - let content_type = content_inspector::inspect(&buffer); - // HACK: We only support UTF-8 at the moment - if content_type.is_binary() - || (content_type != content_inspector::ContentType::UTF_8_BOM - && content_type != content_inspector::ContentType::UTF_8) - { - // HACK: we don't support alternative encodings atm - let msg = report::BinaryFile { path }; - reporter.report(msg.into()); - return Ok(typos_found); - } + let buffer = read_file(path)?; + let (buffer, content_type) = massage_data(buffer)?; + if !explicit && !self.binary && content_type.is_binary() { + let msg = report::BinaryFile { path }; + reporter.report(msg.into()); + return Ok(typos_found); } for (line_idx, line) in buffer.lines().enumerate() { @@ -318,7 +296,7 @@ impl Checks { Some(Status::Valid) => {} Some(corrections) => { let byte_offset = ident.offset(); - let msg = report::Correction { + let msg = report::FileTypo { path, line, line_num, @@ -334,7 +312,7 @@ impl Checks { Some(Status::Valid) => {} Some(corrections) => { let byte_offset = word.offset(); - let msg = report::Correction { + let msg = report::FileTypo { path, line, line_num, @@ -355,3 +333,22 @@ impl Checks { Ok(typos_found) } } + +fn read_file(path: &std::path::Path) -> Result, crate::Error> { + std::fs::read(path).map_err(|e| crate::ErrorKind::IoError.into_error().with_source(e)) +} + +fn massage_data( + buffer: Vec, +) -> Result<(Vec, content_inspector::ContentType), crate::Error> { + let mut content_type = content_inspector::inspect(&buffer); + + // HACK: We only support UTF-8 at the moment + if content_type != content_inspector::ContentType::UTF_8_BOM + && content_type != content_inspector::ContentType::UTF_8 + { + content_type = content_inspector::ContentType::BINARY; + } + + Ok((buffer, content_type)) +} diff --git a/crates/typos/src/report.rs b/crates/typos/src/report.rs index e1cabf4..3626ebd 100644 --- a/crates/typos/src/report.rs +++ b/crates/typos/src/report.rs @@ -8,8 +8,8 @@ use std::io::{self, Write}; #[non_exhaustive] pub enum Message<'m> { BinaryFile(BinaryFile<'m>), - Correction(Correction<'m>), - PathCorrection(PathCorrection<'m>), + FileTypo(FileTypo<'m>), + PathTypo(PathTypo<'m>), File(File<'m>), Parse(Parse<'m>), PathError(PathError<'m>), @@ -20,8 +20,8 @@ impl<'m> Message<'m> { pub fn is_correction(&self) -> bool { match self { Message::BinaryFile(_) => false, - Message::Correction(c) => c.corrections.is_correction(), - Message::PathCorrection(c) => c.corrections.is_correction(), + Message::FileTypo(c) => c.corrections.is_correction(), + Message::PathTypo(c) => c.corrections.is_correction(), Message::File(_) => false, Message::Parse(_) => false, Message::PathError(_) => false, @@ -32,8 +32,8 @@ impl<'m> Message<'m> { pub fn is_error(&self) -> bool { match self { Message::BinaryFile(_) => false, - Message::Correction(_) => false, - Message::PathCorrection(_) => false, + Message::FileTypo(_) => false, + Message::PathTypo(_) => false, Message::File(_) => false, Message::Parse(_) => false, Message::PathError(_) => true, @@ -51,7 +51,7 @@ pub struct BinaryFile<'m> { #[derive(Clone, Debug, serde::Serialize, derive_setters::Setters)] #[non_exhaustive] -pub struct Correction<'m> { +pub struct FileTypo<'m> { pub path: &'m std::path::Path, #[serde(skip)] pub line: &'m [u8], @@ -61,7 +61,7 @@ pub struct Correction<'m> { pub corrections: crate::Status<'m>, } -impl<'m> Default for Correction<'m> { +impl<'m> Default for FileTypo<'m> { fn default() -> Self { Self { path: std::path::Path::new("-"), @@ -76,14 +76,14 @@ impl<'m> Default for Correction<'m> { #[derive(Clone, Debug, serde::Serialize, derive_setters::Setters)] #[non_exhaustive] -pub struct PathCorrection<'m> { +pub struct PathTypo<'m> { pub path: &'m std::path::Path, pub byte_offset: usize, pub typo: &'m str, pub corrections: crate::Status<'m>, } -impl<'m> Default for PathCorrection<'m> { +impl<'m> Default for PathTypo<'m> { fn default() -> Self { Self { path: std::path::Path::new("-"), @@ -195,7 +195,7 @@ impl Report for PrintBrief { Message::BinaryFile(msg) => { log::info!("{}", msg); } - Message::Correction(msg) => match &msg.corrections { + Message::FileTypo(msg) => match &msg.corrections { crate::Status::Valid => {} crate::Status::Invalid => { println!( @@ -217,7 +217,7 @@ impl Report for PrintBrief { ); } }, - Message::PathCorrection(msg) => match &msg.corrections { + Message::PathTypo(msg) => match &msg.corrections { crate::Status::Valid => {} crate::Status::Invalid => { println!("{}: {} is disallowed", msg.path.display(), msg.typo,); @@ -257,8 +257,8 @@ impl Report for PrintLong { Message::BinaryFile(msg) => { log::info!("{}", msg); } - Message::Correction(msg) => print_long_correction(msg), - Message::PathCorrection(msg) => match &msg.corrections { + Message::FileTypo(msg) => print_long_correction(msg), + Message::PathTypo(msg) => match &msg.corrections { crate::Status::Valid => {} crate::Status::Invalid => { println!( @@ -293,7 +293,7 @@ impl Report for PrintLong { } } -fn print_long_correction(msg: &Correction) { +fn print_long_correction(msg: &FileTypo) { let line_num = msg.line_num.to_string(); let line_indent: String = itertools::repeat_n(" ", line_num.len()).collect(); diff --git a/docs/reference.md b/docs/reference.md index 16b0c19..3c068c1 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -8,14 +8,13 @@ Configuration is read from the following (in precedence order) - Command line arguments - File specified via `--config PATH` -- Search parents of specified file / directory for `typos.toml` +- Search parents of specified file / directory for one of `typos.toml`, `_typos.toml`, or `.typos.toml` ### Config Fields | Field | Argument | Format | Description | |------------------------|-------------------|--------|-------------| | files.binary | --binary | bool | Check binary files as text | -| files.ignore-patterns | | list of strings | Typos-specific ignore globs (gitignore syntax) | | files.ignore-hidden | --hidden | bool | Skip hidden files and directories. | | files.ignore-files | --ignore | bool | Respect ignore files. | | files.ignore-dot | --ignore-dot | bool | Respect .ignore files. | diff --git a/src/config.rs b/src/config.rs index cbd599d..5f4e613 100644 --- a/src/config.rs +++ b/src/config.rs @@ -17,16 +17,6 @@ pub trait WalkSource { None } - /// The root for `ignore_patterns` - fn ignore_root(&self) -> Option<&std::path::Path> { - None - } - - /// Ignore the specified patterns (gitignore syntax) - fn ignore_patterns(&self) -> Option<&[String]> { - None - } - /// Skip hidden files and directories. fn ignore_hidden(&self) -> Option { None @@ -120,9 +110,7 @@ impl Config { let mut file = std::fs::File::open(path)?; let mut s = String::new(); file.read_to_string(&mut s)?; - let mut c = Self::from_toml(&s)?; - c.files.ignore_root = path.parent().map(|p| p.to_owned()); - Ok(c) + Self::from_toml(&s) } pub fn from_toml(data: &str) -> Result { @@ -131,7 +119,7 @@ impl Config { } pub fn derive(cwd: &std::path::Path) -> Result { - if let Some(path) = find_project_file(cwd.to_owned(), "typos.toml") { + if let Some(path) = find_project_file(cwd, &["typos.toml", "_typos.toml", ".typos.toml"]) { Self::from_file(&path) } else { Ok(Default::default()) @@ -152,6 +140,10 @@ impl ConfigSource for Config { fn walk(&self) -> Option<&dyn WalkSource> { Some(&self.files) } + + fn default(&self) -> Option<&dyn FileSource> { + Some(&self.default) + } } #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] @@ -159,9 +151,6 @@ impl ConfigSource for Config { #[serde(rename_all = "kebab-case")] pub struct Walk { pub binary: Option, - #[serde(skip)] - pub ignore_root: Option, - pub ignore_patterns: Option>, pub ignore_hidden: Option, pub ignore_files: Option, pub ignore_dot: Option, @@ -175,10 +164,6 @@ impl Walk { if let Some(source) = source.binary() { self.binary = Some(source); } - if let (Some(root), Some(source)) = (source.ignore_root(), source.ignore_patterns()) { - self.ignore_root = Some(root.to_owned()); - self.ignore_patterns = Some(source.to_owned()); - } if let Some(source) = source.ignore_hidden() { self.ignore_hidden = Some(source); } @@ -208,14 +193,6 @@ impl Walk { self.binary.unwrap_or(false) } - pub fn ignore_root(&self) -> Option<&std::path::Path> { - self.ignore_root.as_deref() - } - - pub fn ignore_patterns(&self) -> Option<&[String]> { - self.ignore_patterns.as_deref() - } - pub fn ignore_hidden(&self) -> bool { self.ignore_hidden.unwrap_or(true) } @@ -245,14 +222,6 @@ impl WalkSource for Walk { self.binary } - fn ignore_root(&self) -> Option<&std::path::Path> { - self.ignore_root.as_deref() - } - - fn ignore_patterns(&self) -> Option<&[String]> { - self.ignore_patterns.as_deref() - } - fn ignore_hidden(&self) -> Option { self.ignore_hidden } @@ -431,18 +400,17 @@ impl FileSource for FileConfig { } } -fn find_project_file(dir: std::path::PathBuf, name: &str) -> Option { - let mut file_path = dir; - file_path.push(name); - while !file_path.exists() { - file_path.pop(); // filename - let hit_bottom = !file_path.pop(); - if hit_bottom { - return None; +fn find_project_file(dir: &std::path::Path, names: &[&str]) -> Option { + for ancestor in dir.ancestors() { + let mut file_path = ancestor.join("placeholder"); + for name in names { + file_path.set_file_name(name); + if file_path.exists() { + return Some(file_path); + } } - file_path.push(name); } - Some(file_path) + None } #[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)] diff --git a/src/main.rs b/src/main.rs index 402d933..dc3d227 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,13 @@ mod dict; mod replace; fn main() { - let code = run().unwrap(); + let code = match run() { + Ok(code) => code, + Err(err) => { + eprintln!("{}", err); + 1 + } + }; std::process::exit(code); } @@ -78,16 +84,6 @@ fn run() -> Result { .git_ignore(config.files.ignore_vcs()) .git_exclude(config.files.ignore_vcs()) .parents(config.files.ignore_parent()); - if let (Some(root), Some(patterns)) = - (config.files.ignore_root(), config.files.ignore_patterns()) - { - let mut overrides = ignore::overrides::OverrideBuilder::new(root); - for pattern in patterns { - overrides.add(pattern)?; - } - let overrides = overrides.build()?; - walk.overrides(overrides); - } let mut reporter = args.format.reporter(); let replace_reporter = replace::Replace::new(reporter); diff --git a/src/replace.rs b/src/replace.rs index 80ac6bb..0e91c07 100644 --- a/src/replace.rs +++ b/src/replace.rs @@ -57,7 +57,7 @@ impl<'r> Replace<'r> { impl<'r> typos::report::Report for Replace<'r> { fn report(&self, msg: typos::report::Message<'_>) -> bool { match msg { - typos::report::Message::Correction(msg) => match msg.corrections { + typos::report::Message::FileTypo(msg) => match msg.corrections { typos::Status::Corrections(corrections) if corrections.len() == 1 => { let path = msg.path.to_owned(); let line_num = msg.line_num; @@ -73,11 +73,9 @@ impl<'r> typos::report::Report for Replace<'r> { content.push(correction); false } - _ => self - .reporter - .report(typos::report::Message::Correction(msg)), + _ => self.reporter.report(typos::report::Message::FileTypo(msg)), }, - typos::report::Message::PathCorrection(msg) => match msg.corrections { + typos::report::Message::PathTypo(msg) => match msg.corrections { typos::Status::Corrections(corrections) if corrections.len() == 1 => { let path = msg.path.to_owned(); let correction = @@ -87,9 +85,7 @@ impl<'r> typos::report::Report for Replace<'r> { content.push(correction); false } - _ => self - .reporter - .report(typos::report::Message::PathCorrection(msg)), + _ => self.reporter.report(typos::report::Message::PathTypo(msg)), }, _ => self.reporter.report(msg), } @@ -208,7 +204,7 @@ mod test { let primary = typos::report::PrintSilent; let replace = Replace::new(&primary); replace.report( - typos::report::Correction::default() + typos::report::FileTypo::default() .path(input_file.path()) .line(b"1 foo 2\n3 4 5") .line_num(1) @@ -233,7 +229,7 @@ mod test { let primary = typos::report::PrintSilent; let replace = Replace::new(&primary); replace.report( - typos::report::PathCorrection::default() + typos::report::PathTypo::default() .path(input_file.path()) .byte_offset(0) .typo("foo")