From 6ae42b4c1e72f9274fc2bbdc86b57a3b7ffd4140 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 07:24:22 -0500 Subject: [PATCH 01/20] refactor(parse): Explicit Default --- src/tokens.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tokens.rs b/src/tokens.rs index e91824a..86e9a82 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -6,7 +6,7 @@ pub enum Case { None, } -#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ParserBuilder { ignore_hex: bool, } @@ -33,6 +33,12 @@ impl ParserBuilder { } } +impl Default for ParserBuilder { + fn default() -> Self { + Self { ignore_hex: false } + } +} + #[derive(Debug, Clone)] pub struct Parser { words_str: regex::Regex, From 50c89ef761ac15c57b5e00d2479e3e1fa39a96a9 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 07:24:54 -0500 Subject: [PATCH 02/20] fix(parse): Change ignore_hex default --- src/tokens.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokens.rs b/src/tokens.rs index 86e9a82..aa2f138 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -35,7 +35,7 @@ impl ParserBuilder { impl Default for ParserBuilder { fn default() -> Self { - Self { ignore_hex: false } + Self { ignore_hex: true } } } From e093135ac1aaae74f92a6092d2b8437dda74fbb7 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 07:26:32 -0500 Subject: [PATCH 03/20] feat(parse): Make digits in identifier optional --- src/tokens.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/tokens.rs b/src/tokens.rs index aa2f138..d2fec14 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -9,6 +9,7 @@ pub enum Case { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ParserBuilder { ignore_hex: bool, + include_digits: bool, } impl ParserBuilder { @@ -21,10 +22,19 @@ impl ParserBuilder { self } + pub fn include_digits(&mut self, yes: bool) -> &mut Self { + self.include_digits = yes; + self + } + pub fn build(&self) -> Parser { - let pattern = r#"\b(\p{Alphabetic}|\d|_|')+\b"#; - let words_str = regex::Regex::new(pattern).unwrap(); - let words_bytes = regex::bytes::Regex::new(pattern).unwrap(); + let mut pattern = r#"\b(\p{Alphabetic}|_|'"#.to_owned(); + if self.include_digits { + pattern.push_str(r#"|\d"#); + } + pattern.push_str(r#")+\b"#); + let words_str = regex::Regex::new(&pattern).unwrap(); + let words_bytes = regex::bytes::Regex::new(&pattern).unwrap(); Parser { words_str, words_bytes, @@ -35,7 +45,10 @@ impl ParserBuilder { impl Default for ParserBuilder { fn default() -> Self { - Self { ignore_hex: true } + Self { + ignore_hex: true, + include_digits: true, + } } } From 3419a8df85a88c3e073ef04ca64e381e844f2849 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 07:36:27 -0500 Subject: [PATCH 04/20] feat(parse): Make identifier symbols configurable --- Cargo.lock | 7 ++++--- Cargo.toml | 1 + src/tokens.rs | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8856495..d13e961 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,7 +271,7 @@ name = "heck" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -811,6 +811,7 @@ dependencies = [ "serde_json 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -828,7 +829,7 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1013,7 +1014,7 @@ dependencies = [ "checksum treeline 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" -"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" +"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" diff --git a/Cargo.toml b/Cargo.toml index daef3c6..921ec74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ unicase = "1.1" bstr = "0.2" log = "0.4" env_logger = "0.6" +unicode-segmentation = "1.3.0" [dev-dependencies] assert_fs = "0.10" diff --git a/src/tokens.rs b/src/tokens.rs index d2fec14..421c59c 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -10,6 +10,7 @@ pub enum Case { pub struct ParserBuilder { ignore_hex: bool, include_digits: bool, + include_chars: String, } impl ParserBuilder { @@ -27,11 +28,22 @@ impl ParserBuilder { self } + pub fn include_chars(&mut self, chars: String) -> &mut Self { + self.include_chars = chars; + self + } + pub fn build(&self) -> Parser { - let mut pattern = r#"\b(\p{Alphabetic}|_|'"#.to_owned(); + let mut pattern = r#"\b(\p{Alphabetic}"#.to_owned(); if self.include_digits { pattern.push_str(r#"|\d"#); } + for grapheme in + unicode_segmentation::UnicodeSegmentation::graphemes(self.include_chars.as_str(), true) + { + let escaped = regex::escape(&grapheme); + pattern.push_str(&format!("|{}", escaped)); + } pattern.push_str(r#")+\b"#); let words_str = regex::Regex::new(&pattern).unwrap(); let words_bytes = regex::bytes::Regex::new(&pattern).unwrap(); @@ -48,6 +60,7 @@ impl Default for ParserBuilder { Self { ignore_hex: true, include_digits: true, + include_chars: "_'".to_owned(), } } } From a129fb3d657a6fce8c4ac3e2da2f90334e409875 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 08:16:22 -0500 Subject: [PATCH 05/20] refactor(report): Switch to swrde derive feature --- Cargo.lock | 4 +++- Cargo.toml | 3 +-- src/lib.rs | 3 --- src/report.rs | 8 ++++---- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d13e961..ec288fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -651,6 +651,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "serde" version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "serde_derive" @@ -807,7 +810,6 @@ dependencies = [ "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 921ec74..9f4d73e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,8 +28,7 @@ ignore = "0.4" phf = { version = "0.7", features = ["unicase"] } regex = "1.0" lazy_static = "1.2.0" -serde = "1.0" -serde_derive = "1.0" +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" itertools = "0.8" unicase = "1.1" diff --git a/src/lib.rs b/src/lib.rs index b5201df..2c3fd94 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,3 @@ -#[macro_use] -extern crate serde_derive; - mod dict; mod dict_codegen; diff --git a/src/report.rs b/src/report.rs index 23b5c47..70ac51c 100644 --- a/src/report.rs +++ b/src/report.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::io::{self, Write}; -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, serde::Serialize)] #[serde(rename_all = "snake_case")] #[serde(tag = "type")] pub enum Message<'m> { @@ -28,14 +28,14 @@ impl<'m> From> for Message<'m> { } } -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, serde::Serialize)] pub struct BinaryFile<'m> { pub path: &'m std::path::Path, #[serde(skip)] pub(crate) non_exhaustive: (), } -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, serde::Serialize)] pub struct Correction<'m> { pub path: &'m std::path::Path, #[serde(skip)] @@ -48,7 +48,7 @@ pub struct Correction<'m> { pub(crate) non_exhaustive: (), } -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, serde::Serialize)] pub struct FilenameCorrection<'m> { pub path: &'m std::path::Path, pub typo: &'m str, From e90a89ef933338112806663e62585a492fc75e93 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 08:20:18 -0500 Subject: [PATCH 06/20] refactor(report): Leverage derive_more --- Cargo.lock | 27 +++++++++++++++++++++------ Cargo.toml | 1 + src/report.rs | 20 +------------------- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec288fd..3286311 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,7 +75,7 @@ name = "bstr" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex-automata 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -164,6 +164,19 @@ dependencies = [ "memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "derive_more" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "difference" version = "2.0.0" @@ -289,7 +302,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-channel 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -314,7 +327,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lazy_static" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -783,7 +796,7 @@ name = "thread_local" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -800,11 +813,12 @@ dependencies = [ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap-verbosity-flag 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "ignore 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", @@ -938,6 +952,7 @@ dependencies = [ "checksum crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "41ee4864f4797060e52044376f7d107429ce1fb43460021b126424b7180ee21a" "checksum csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd1c44c58078cfbeaf11fbb3eac9ae5534c23004ed770cc4bfb48e658ae4f04" "checksum csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5cdef62f37e6ffe7d1f07a381bc0db32b7a3ff1cac0de56cb0d81e71f53d65" +"checksum derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe" "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" "checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" @@ -955,7 +970,7 @@ dependencies = [ "checksum ignore 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ad03ca67dc12474ecd91fdb94d758cbd20cb4e7a78ebe831df26a9b7511e1162" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" -"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" +"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum libc 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)" = "48450664a984b25d5b479554c29cc04e3150c97aa4c01da5604a2d4ed9151476" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" diff --git a/Cargo.toml b/Cargo.toml index 9f4d73e..bc9cb67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ bstr = "0.2" log = "0.4" env_logger = "0.6" unicode-segmentation = "1.3.0" +derive_more = "0.15.0" [dev-dependencies] assert_fs = "0.10" diff --git a/src/report.rs b/src/report.rs index 70ac51c..96f0957 100644 --- a/src/report.rs +++ b/src/report.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::io::{self, Write}; -#[derive(Clone, Debug, serde::Serialize)] +#[derive(Clone, Debug, serde::Serialize, derive_more::From)] #[serde(rename_all = "snake_case")] #[serde(tag = "type")] pub enum Message<'m> { @@ -10,24 +10,6 @@ pub enum Message<'m> { FilenameCorrection(FilenameCorrection<'m>), } -impl<'m> From> for Message<'m> { - fn from(msg: BinaryFile<'m>) -> Self { - Message::BinaryFile(msg) - } -} - -impl<'m> From> for Message<'m> { - fn from(msg: Correction<'m>) -> Self { - Message::Correction(msg) - } -} - -impl<'m> From> for Message<'m> { - fn from(msg: FilenameCorrection<'m>) -> Self { - Message::FilenameCorrection(msg) - } -} - #[derive(Clone, Debug, serde::Serialize)] pub struct BinaryFile<'m> { pub path: &'m std::path::Path, From f15191de14fa27d22e1cb6b9317437f0404abe51 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 08:25:55 -0500 Subject: [PATCH 07/20] refactor(report): Leverage derive_more, more --- src/report.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/report.rs b/src/report.rs index 96f0957..0c6adc3 100644 --- a/src/report.rs +++ b/src/report.rs @@ -10,7 +10,8 @@ pub enum Message<'m> { FilenameCorrection(FilenameCorrection<'m>), } -#[derive(Clone, Debug, serde::Serialize)] +#[derive(Clone, Debug, serde::Serialize, derive_more::Display)] +#[display(fmt = "Skipping binary file {}", "path.display()")] pub struct BinaryFile<'m> { pub path: &'m std::path::Path, #[serde(skip)] @@ -46,7 +47,7 @@ pub fn print_silent(_: Message) {} pub fn print_brief(msg: Message) { match msg { Message::BinaryFile(msg) => { - println!("Skipping binary file {}", msg.path.display(),); + println!("{}", msg); } Message::Correction(msg) => { println!( @@ -67,7 +68,7 @@ pub fn print_brief(msg: Message) { pub fn print_long(msg: Message) { match msg { Message::BinaryFile(msg) => { - println!("Skipping binary file {}", msg.path.display(),); + println!("{}", msg); } Message::Correction(msg) => print_long_correction(msg), Message::FilenameCorrection(msg) => { From 8d96a2ad1d170df7b7723bd4b09e2a0bd3838508 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 09:40:06 -0500 Subject: [PATCH 08/20] refactor(cli): Prepare for merging im config file --- src/config.rs | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 70 ++++++++++++++-------------- 2 files changed, 162 insertions(+), 34 deletions(-) create mode 100644 src/config.rs diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..572d4db --- /dev/null +++ b/src/config.rs @@ -0,0 +1,126 @@ +pub trait ConfigSource { + /// Skip hidden files and directories. + fn ignore_hidden(&self) -> Option { + None + } + + /// Respect ignore files. + fn ignore_files(&self) -> Option { + None + } + + /// Respect .ignore files. + fn ignore_dot(&self) -> Option { + None + } + + /// Respect ignore files in vcs directories. + fn ignore_vcs(&self) -> Option { + None + } + + /// Respect global ignore files. + fn ignore_global(&self) -> Option { + None + } + + /// Respect ignore files in parent directories. + fn ignore_parent(&self) -> Option { + None + } +} + +#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields, default)] +#[serde(rename_all = "kebab-case")] +pub struct Config { + pub ignore_hidden: Option, + pub ignore_files: Option, + pub ignore_dot: Option, + pub ignore_vcs: Option, + pub ignore_global: Option, + pub ignore_parent: Option, +} + +impl Config { + pub fn update(&mut self, source: &dyn ConfigSource) { + if let Some(source) = source.ignore_hidden() { + self.ignore_hidden = Some(source); + } + if let Some(source) = source.ignore_files() { + self.ignore_files = Some(source); + self.ignore_dot = None; + self.ignore_vcs = None; + self.ignore_global = None; + self.ignore_parent = None; + } + if let Some(source) = source.ignore_dot() { + self.ignore_dot = Some(source); + } + if let Some(source) = source.ignore_vcs() { + self.ignore_vcs = Some(source); + self.ignore_global = None; + } + if let Some(source) = source.ignore_global() { + self.ignore_global = Some(source); + } + if let Some(source) = source.ignore_parent() { + self.ignore_parent = Some(source); + } + } + + pub fn ignore_hidden(&self) -> bool { + self.ignore_hidden.unwrap_or(true) + } + + pub fn ignore_dot(&self) -> bool { + self.ignore_dot + .or_else(|| self.ignore_files) + .unwrap_or(true) + } + + pub fn ignore_vcs(&self) -> bool { + self.ignore_vcs + .or_else(|| self.ignore_files) + .unwrap_or(true) + } + + pub fn ignore_global(&self) -> bool { + self.ignore_global + .or_else(|| self.ignore_vcs) + .or_else(|| self.ignore_files) + .unwrap_or(true) + } + + pub fn ignore_parent(&self) -> bool { + self.ignore_parent + .or_else(|| self.ignore_files) + .unwrap_or(true) + } +} + +impl ConfigSource for Config { + fn ignore_hidden(&self) -> Option { + self.ignore_hidden + } + + fn ignore_files(&self) -> Option { + self.ignore_files + } + + fn ignore_dot(&self) -> Option { + self.ignore_dot + } + + fn ignore_vcs(&self) -> Option { + self.ignore_vcs + } + + fn ignore_global(&self) -> Option { + self.ignore_global + } + + fn ignore_parent(&self) -> Option { + self.ignore_parent + } +} diff --git a/src/main.rs b/src/main.rs index 456a054..e15ff70 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,8 @@ use std::io::Write; use structopt::StructOpt; +mod config; + arg_enum! { #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum Format { @@ -167,8 +169,10 @@ impl Options { (_, _) => unreachable!("StructOpt should make this impossible"), } } +} - pub fn ignore_hidden(&self) -> Option { +impl config::ConfigSource for Options { + fn ignore_hidden(&self) -> Option { match (self.hidden, self.no_hidden) { (true, false) => Some(false), (false, true) => Some(true), @@ -177,49 +181,44 @@ impl Options { } } - pub fn ignore_dot(&self) -> Option { + fn ignore_files(&self) -> Option { + match (self.no_ignore, self.ignore) { + (true, false) => Some(false), + (false, true) => Some(true), + (false, false) => None, + (_, _) => unreachable!("StructOpt should make this impossible"), + } + } + + fn ignore_dot(&self) -> Option { match (self.no_ignore_dot, self.ignore_dot) { (true, false) => Some(false), (false, true) => Some(true), (false, false) => None, (_, _) => unreachable!("StructOpt should make this impossible"), } - .or_else(|| self.ignore_files()) } - pub fn ignore_global(&self) -> Option { - match (self.no_ignore_global, self.ignore_global) { - (true, false) => Some(false), - (false, true) => Some(true), - (false, false) => None, - (_, _) => unreachable!("StructOpt should make this impossible"), - } - .or_else(|| self.ignore_vcs()) - .or_else(|| self.ignore_files()) - } - - pub fn ignore_parent(&self) -> Option { - match (self.no_ignore_parent, self.ignore_parent) { - (true, false) => Some(false), - (false, true) => Some(true), - (false, false) => None, - (_, _) => unreachable!("StructOpt should make this impossible"), - } - .or_else(|| self.ignore_files()) - } - - pub fn ignore_vcs(&self) -> Option { + fn ignore_vcs(&self) -> Option { match (self.no_ignore_vcs, self.ignore_vcs) { (true, false) => Some(false), (false, true) => Some(true), (false, false) => None, (_, _) => unreachable!("StructOpt should make this impossible"), } - .or_else(|| self.ignore_files()) } - fn ignore_files(&self) -> Option { - match (self.no_ignore, self.ignore) { + fn ignore_global(&self) -> Option { + match (self.no_ignore_global, self.ignore_global) { + (true, false) => Some(false), + (false, true) => Some(true), + (false, false) => None, + (_, _) => unreachable!("StructOpt should make this impossible"), + } + } + + fn ignore_parent(&self) -> Option { + match (self.no_ignore_parent, self.ignore_parent) { (true, false) => Some(false), (false, true) => Some(true), (false, false) => None, @@ -252,6 +251,9 @@ pub fn get_logging(level: log::Level) -> env_logger::Builder { fn run() -> Result { let options = Options::from_args().infer(); + let mut config = config::Config::default(); + config.update(&options); + let mut builder = get_logging(options.verbose.log_level()); builder.init(); @@ -280,12 +282,12 @@ fn run() -> Result { for path in &options.path[1..] { walk.add(path); } - walk.hidden(options.ignore_hidden().unwrap_or(true)) - .ignore(options.ignore_dot().unwrap_or(true)) - .git_global(options.ignore_global().unwrap_or(true)) - .git_ignore(options.ignore_vcs().unwrap_or(true)) - .git_exclude(options.ignore_vcs().unwrap_or(true)) - .parents(options.ignore_parent().unwrap_or(true)); + walk.hidden(config.ignore_hidden()) + .ignore(config.ignore_dot()) + .git_global(config.ignore_global()) + .git_ignore(config.ignore_vcs()) + .git_exclude(config.ignore_vcs()) + .parents(config.ignore_parent()); let mut typos_found = false; for entry in walk.build() { let entry = entry?; From 3d4da686ad8d3d02b3a5224cecbe0c4a9184da99 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 09:55:17 -0500 Subject: [PATCH 09/20] feat: Accept config on command-line --- Cargo.lock | 10 ++++++++++ Cargo.toml | 1 + src/main.rs | 12 ++++++++++++ 3 files changed, 23 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 3286311..daefb43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -799,6 +799,14 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "toml" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "treeline" version = "0.1.0" @@ -826,6 +834,7 @@ dependencies = [ "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1028,6 +1037,7 @@ dependencies = [ "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" "checksum treeline 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" diff --git a/Cargo.toml b/Cargo.toml index bc9cb67..4bb7a8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ regex = "1.0" lazy_static = "1.2.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +toml = "0.4" itertools = "0.8" unicase = "1.1" bstr = "0.2" diff --git a/src/main.rs b/src/main.rs index e15ff70..48b7d87 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ #[macro_use] extern crate clap; +use std::io::Read; use std::io::Write; use structopt::StructOpt; @@ -42,6 +43,10 @@ struct Options { /// Paths to check path: Vec, + #[structopt(short = "c", long = "config")] + /// Custom config file + custom_config: Option, + #[structopt(long, raw(overrides_with = r#""check-filenames""#))] /// Skip verifying spelling in file names. no_check_filenames: bool, @@ -252,6 +257,13 @@ fn run() -> Result { let options = Options::from_args().infer(); let mut config = config::Config::default(); + if let Some(path) = options.custom_config.as_ref() { + let mut file = std::fs::File::open(path)?; + let mut s = String::new(); + file.read_to_string(&mut s)?; + let custom: config::Config = toml::from_str(&s)?; + config.update(&custom); + } config.update(&options); let mut builder = get_logging(options.verbose.log_level()); From ad4c6dcd77ddb412d3c0882149ffc41f9e2fad77 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 10:05:06 -0500 Subject: [PATCH 10/20] refactor(config): Centralize loading logic --- src/config.rs | 14 ++++++++++++++ src/main.rs | 8 ++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/config.rs b/src/config.rs index 572d4db..d6284f6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,5 @@ +use std::io::Read; + pub trait ConfigSource { /// Skip hidden files and directories. fn ignore_hidden(&self) -> Option { @@ -43,6 +45,18 @@ pub struct Config { } impl Config { + pub fn from_file(path: &std::path::Path) -> Result { + let mut file = std::fs::File::open(path)?; + let mut s = String::new(); + file.read_to_string(&mut s)?; + Self::from_toml(&s) + } + + pub fn from_toml(data: &str) -> Result { + let content = toml::from_str(data)?; + Ok(content) + } + pub fn update(&mut self, source: &dyn ConfigSource) { if let Some(source) = source.ignore_hidden() { self.ignore_hidden = Some(source); diff --git a/src/main.rs b/src/main.rs index 48b7d87..ffef65b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,6 @@ #[macro_use] extern crate clap; -use std::io::Read; use std::io::Write; use structopt::StructOpt; @@ -45,7 +44,7 @@ struct Options { #[structopt(short = "c", long = "config")] /// Custom config file - custom_config: Option, + custom_config: Option, #[structopt(long, raw(overrides_with = r#""check-filenames""#))] /// Skip verifying spelling in file names. @@ -258,10 +257,7 @@ fn run() -> Result { let mut config = config::Config::default(); if let Some(path) = options.custom_config.as_ref() { - let mut file = std::fs::File::open(path)?; - let mut s = String::new(); - file.read_to_string(&mut s)?; - let custom: config::Config = toml::from_str(&s)?; + let custom = config::Config::from_file(path)?; config.update(&custom); } config.update(&options); From 87015d3522526491a5b5d81d5f2e1ca4a2f00898 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 10:16:57 -0500 Subject: [PATCH 11/20] feat(config): Find config for each path passed in --- src/config.rs | 22 ++++++++++++++++ src/main.rs | 72 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 65 insertions(+), 29 deletions(-) diff --git a/src/config.rs b/src/config.rs index d6284f6..95bba4a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -57,6 +57,14 @@ impl Config { Ok(content) } + pub fn derive(cwd: &std::path::Path) -> Result { + if let Some(path) = find_project_file(cwd.to_owned(), "typos.toml") { + Self::from_file(&path) + } else { + Ok(Default::default()) + } + } + pub fn update(&mut self, source: &dyn ConfigSource) { if let Some(source) = source.ignore_hidden() { self.ignore_hidden = Some(source); @@ -138,3 +146,17 @@ impl ConfigSource for Config { self.ignore_parent } } + +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; + } + file_path.push(name); + } + Some(file_path) +} diff --git a/src/main.rs b/src/main.rs index ffef65b..29add46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -46,6 +46,10 @@ struct Options { /// Custom config file custom_config: Option, + #[structopt(long = "isolated")] + /// Ignore implicit configuration files. + isolated: bool, + #[structopt(long, raw(overrides_with = r#""check-filenames""#))] /// Skip verifying spelling in file names. no_check_filenames: bool, @@ -255,13 +259,6 @@ pub fn get_logging(level: log::Level) -> env_logger::Builder { fn run() -> Result { let options = Options::from_args().infer(); - let mut config = config::Config::default(); - if let Some(path) = options.custom_config.as_ref() { - let custom = config::Config::from_file(path)?; - config.update(&custom); - } - config.update(&options); - let mut builder = get_logging(options.verbose.log_level()); builder.init(); @@ -282,30 +279,47 @@ fn run() -> Result { .binary(binary) .build(&dictionary, &parser); - let first_path = &options - .path - .get(0) - .expect("arg parsing enforces at least one"); - let mut walk = ignore::WalkBuilder::new(first_path); - for path in &options.path[1..] { - walk.add(path); + let mut config = config::Config::default(); + if let Some(path) = options.custom_config.as_ref() { + let custom = config::Config::from_file(path)?; + config.update(&custom); } - walk.hidden(config.ignore_hidden()) - .ignore(config.ignore_dot()) - .git_global(config.ignore_global()) - .git_ignore(config.ignore_vcs()) - .git_exclude(config.ignore_vcs()) - .parents(config.ignore_parent()); + let config = config; + let mut typos_found = false; - for entry in walk.build() { - let entry = entry?; - if entry.file_type().map(|t| t.is_file()).unwrap_or(true) { - let explicit = entry.depth() == 0; - if checks.check_filename(entry.path(), options.format.report())? { - typos_found = true; - } - if checks.check_file(entry.path(), explicit, options.format.report())? { - typos_found = true; + for path in options.path.iter() { + let path = path.canonicalize()?; + let cwd = if path.is_file() { + path.parent().unwrap() + } else { + path.as_path() + }; + + let mut config = config.clone(); + if !options.isolated { + let derived = config::Config::derive(cwd)?; + config.update(&derived); + } + config.update(&options); + let config = config; + + let mut walk = ignore::WalkBuilder::new(path); + walk.hidden(config.ignore_hidden()) + .ignore(config.ignore_dot()) + .git_global(config.ignore_global()) + .git_ignore(config.ignore_vcs()) + .git_exclude(config.ignore_vcs()) + .parents(config.ignore_parent()); + for entry in walk.build() { + let entry = entry?; + if entry.file_type().map(|t| t.is_file()).unwrap_or(true) { + let explicit = entry.depth() == 0; + if checks.check_filename(entry.path(), options.format.report())? { + typos_found = true; + } + if checks.check_file(entry.path(), explicit, options.format.report())? { + typos_found = true; + } } } } From f9a16005133e184cf43385ca7e5c6bc795dedb65 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 10:32:53 -0500 Subject: [PATCH 12/20] refactor( Push out options --- src/main.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main.rs b/src/main.rs index 29add46..48adff5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -262,21 +262,16 @@ fn run() -> Result { let mut builder = get_logging(options.verbose.log_level()); builder.init(); - let check_filenames = options.check_filenames().unwrap_or(true); - let check_files = options.check_files().unwrap_or(true); - let ignore_hex = options.ignore_hex().unwrap_or(true); - let binary = options.binary().unwrap_or(false); - let dictionary = typos::BuiltIn::new(); let parser = typos::tokens::ParserBuilder::new() - .ignore_hex(ignore_hex) + .ignore_hex(options.ignore_hex().unwrap_or(true)) .build(); let checks = typos::checks::CheckSettings::new() - .check_filenames(check_filenames) - .check_files(check_files) - .binary(binary) + .check_filenames(options.check_filenames().unwrap_or(true)) + .check_files(options.check_files().unwrap_or(true)) + .binary(options.binary().unwrap_or(false)) .build(&dictionary, &parser); let mut config = config::Config::default(); From a923f93ec502297fe084d25db939c9058dd3a4b2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 11:05:19 -0500 Subject: [PATCH 13/20] fix(config): Move file-based config into a table --- src/config.rs | 39 +++++++++++++--- src/main.rs | 122 +++++++++++++++++++++++++++++--------------------- 2 files changed, 103 insertions(+), 58 deletions(-) diff --git a/src/config.rs b/src/config.rs index 95bba4a..d93977c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,10 @@ use std::io::Read; pub trait ConfigSource { + fn walk(&self) -> Option<&dyn WalkSource>; +} + +pub trait WalkSource { /// Skip hidden files and directories. fn ignore_hidden(&self) -> Option { None @@ -36,12 +40,7 @@ pub trait ConfigSource { #[serde(deny_unknown_fields, default)] #[serde(rename_all = "kebab-case")] pub struct Config { - pub ignore_hidden: Option, - pub ignore_files: Option, - pub ignore_dot: Option, - pub ignore_vcs: Option, - pub ignore_global: Option, - pub ignore_parent: Option, + pub files: Walk, } impl Config { @@ -66,6 +65,32 @@ impl Config { } pub fn update(&mut self, source: &dyn ConfigSource) { + if let Some(walk) = source.walk() { + self.files.update(walk); + } + } +} + +impl ConfigSource for Config { + fn walk(&self) -> Option<&dyn WalkSource> { + Some(&self.files) + } +} + +#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields, default)] +#[serde(rename_all = "kebab-case")] +pub struct Walk { + pub ignore_hidden: Option, + pub ignore_files: Option, + pub ignore_dot: Option, + pub ignore_vcs: Option, + pub ignore_global: Option, + pub ignore_parent: Option, +} + +impl Walk { + pub fn update(&mut self, source: &dyn WalkSource) { if let Some(source) = source.ignore_hidden() { self.ignore_hidden = Some(source); } @@ -121,7 +146,7 @@ impl Config { } } -impl ConfigSource for Config { +impl WalkSource for Walk { fn ignore_hidden(&self) -> Option { self.ignore_hidden } diff --git a/src/main.rs b/src/main.rs index 48adff5..0284086 100644 --- a/src/main.rs +++ b/src/main.rs @@ -89,49 +89,8 @@ struct Options { #[structopt(long, raw(overrides_with = r#""binary""#), raw(hidden = "true"))] no_binary: bool, - #[structopt(long, raw(overrides_with = r#""no-hidden""#))] - /// Search hidden files and directories. - hidden: bool, - #[structopt(long, raw(overrides_with = r#""hidden""#), raw(hidden = "true"))] - no_hidden: bool, - - #[structopt(long, raw(overrides_with = r#""ignore""#))] - /// Don't respect ignore files. - no_ignore: bool, - #[structopt(long, raw(overrides_with = r#""no-ignore""#), raw(hidden = "true"))] - ignore: bool, - - #[structopt(long, raw(overrides_with = r#""ignore-dot""#))] - /// Don't respect .ignore files. - no_ignore_dot: bool, - #[structopt(long, raw(overrides_with = r#""no-ignore-dot""#), raw(hidden = "true"))] - ignore_dot: bool, - - #[structopt(long, raw(overrides_with = r#""ignore-global""#))] - /// Don't respect global ignore files. - no_ignore_global: bool, - #[structopt( - long, - raw(overrides_with = r#""no-ignore-global""#), - raw(hidden = "true") - )] - ignore_global: bool, - - #[structopt(long, raw(overrides_with = r#""ignore-parent""#))] - /// Don't respect ignore files in parent directories. - no_ignore_parent: bool, - #[structopt( - long, - raw(overrides_with = r#""no-ignore-parent""#), - raw(hidden = "true") - )] - ignore_parent: bool, - - #[structopt(long, raw(overrides_with = r#""ignore-vcs""#))] - /// Don't respect ignore files in vcs directories. - no_ignore_vcs: bool, - #[structopt(long, raw(overrides_with = r#""no-ignore-vcs""#), raw(hidden = "true"))] - ignore_vcs: bool, + #[structopt(flatten)] + config: ConfigArgs, #[structopt(flatten)] verbose: clap_verbosity_flag::Verbosity, @@ -179,7 +138,68 @@ impl Options { } } -impl config::ConfigSource for Options { +#[derive(Debug, StructOpt)] +#[structopt(rename_all = "kebab-case")] +struct ConfigArgs { + #[structopt(flatten)] + walk: WalkArgs, +} + +impl config::ConfigSource for ConfigArgs { + fn walk(&self) -> Option<&dyn config::WalkSource> { + Some(&self.walk) + } +} + +#[derive(Debug, StructOpt)] +#[structopt(rename_all = "kebab-case")] +struct WalkArgs { + #[structopt(long, raw(overrides_with = r#""no-hidden""#))] + /// Search hidden files and directories. + hidden: bool, + #[structopt(long, raw(overrides_with = r#""hidden""#), raw(hidden = "true"))] + no_hidden: bool, + + #[structopt(long, raw(overrides_with = r#""ignore""#))] + /// Don't respect ignore files. + no_ignore: bool, + #[structopt(long, raw(overrides_with = r#""no-ignore""#), raw(hidden = "true"))] + ignore: bool, + + #[structopt(long, raw(overrides_with = r#""ignore-dot""#))] + /// Don't respect .ignore files. + no_ignore_dot: bool, + #[structopt(long, raw(overrides_with = r#""no-ignore-dot""#), raw(hidden = "true"))] + ignore_dot: bool, + + #[structopt(long, raw(overrides_with = r#""ignore-global""#))] + /// Don't respect global ignore files. + no_ignore_global: bool, + #[structopt( + long, + raw(overrides_with = r#""no-ignore-global""#), + raw(hidden = "true") + )] + ignore_global: bool, + + #[structopt(long, raw(overrides_with = r#""ignore-parent""#))] + /// Don't respect ignore files in parent directories. + no_ignore_parent: bool, + #[structopt( + long, + raw(overrides_with = r#""no-ignore-parent""#), + raw(hidden = "true") + )] + ignore_parent: bool, + + #[structopt(long, raw(overrides_with = r#""ignore-vcs""#))] + /// Don't respect ignore files in vcs directories. + no_ignore_vcs: bool, + #[structopt(long, raw(overrides_with = r#""no-ignore-vcs""#), raw(hidden = "true"))] + ignore_vcs: bool, +} + +impl config::WalkSource for WalkArgs { fn ignore_hidden(&self) -> Option { match (self.hidden, self.no_hidden) { (true, false) => Some(false), @@ -295,16 +315,16 @@ fn run() -> Result { let derived = config::Config::derive(cwd)?; config.update(&derived); } - config.update(&options); + config.update(&options.config); let config = config; let mut walk = ignore::WalkBuilder::new(path); - walk.hidden(config.ignore_hidden()) - .ignore(config.ignore_dot()) - .git_global(config.ignore_global()) - .git_ignore(config.ignore_vcs()) - .git_exclude(config.ignore_vcs()) - .parents(config.ignore_parent()); + walk.hidden(config.files.ignore_hidden()) + .ignore(config.files.ignore_dot()) + .git_global(config.files.ignore_global()) + .git_ignore(config.files.ignore_vcs()) + .git_exclude(config.files.ignore_vcs()) + .parents(config.files.ignore_parent()); for entry in walk.build() { let entry = entry?; if entry.file_type().map(|t| t.is_file()).unwrap_or(true) { From 77603daab532250fcfdad21778cb0e941e530837 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 11:09:01 -0500 Subject: [PATCH 14/20] refactor(cli): Rename Options struct --- src/main.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0284086..684ceb2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,7 +37,7 @@ impl Default for Format { #[derive(Debug, StructOpt)] #[structopt(rename_all = "kebab-case")] -struct Options { +struct Args { #[structopt(parse(from_os_str), default_value = ".")] /// Paths to check path: Vec, @@ -96,7 +96,7 @@ struct Options { verbose: clap_verbosity_flag::Verbosity, } -impl Options { +impl Args { pub fn infer(self) -> Self { self } @@ -277,32 +277,32 @@ pub fn get_logging(level: log::Level) -> env_logger::Builder { } fn run() -> Result { - let options = Options::from_args().infer(); + let args = Args::from_args().infer(); - let mut builder = get_logging(options.verbose.log_level()); + let mut builder = get_logging(args.verbose.log_level()); builder.init(); let dictionary = typos::BuiltIn::new(); let parser = typos::tokens::ParserBuilder::new() - .ignore_hex(options.ignore_hex().unwrap_or(true)) + .ignore_hex(args.ignore_hex().unwrap_or(true)) .build(); let checks = typos::checks::CheckSettings::new() - .check_filenames(options.check_filenames().unwrap_or(true)) - .check_files(options.check_files().unwrap_or(true)) - .binary(options.binary().unwrap_or(false)) + .check_filenames(args.check_filenames().unwrap_or(true)) + .check_files(args.check_files().unwrap_or(true)) + .binary(args.binary().unwrap_or(false)) .build(&dictionary, &parser); let mut config = config::Config::default(); - if let Some(path) = options.custom_config.as_ref() { + if let Some(path) = args.custom_config.as_ref() { let custom = config::Config::from_file(path)?; config.update(&custom); } let config = config; let mut typos_found = false; - for path in options.path.iter() { + for path in args.path.iter() { let path = path.canonicalize()?; let cwd = if path.is_file() { path.parent().unwrap() @@ -311,11 +311,11 @@ fn run() -> Result { }; let mut config = config.clone(); - if !options.isolated { + if !args.isolated { let derived = config::Config::derive(cwd)?; config.update(&derived); } - config.update(&options.config); + config.update(&args.config); let config = config; let mut walk = ignore::WalkBuilder::new(path); @@ -329,10 +329,10 @@ fn run() -> Result { let entry = entry?; if entry.file_type().map(|t| t.is_file()).unwrap_or(true) { let explicit = entry.depth() == 0; - if checks.check_filename(entry.path(), options.format.report())? { + if checks.check_filename(entry.path(), args.format.report())? { typos_found = true; } - if checks.check_file(entry.path(), explicit, options.format.report())? { + if checks.check_file(entry.path(), explicit, args.format.report())? { typos_found = true; } } From 29ff040fd1804579341b0cd6a7b6ab35052acf4f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 7 Aug 2019 17:14:29 -0500 Subject: [PATCH 15/20] feat(config): Expose binary in config file --- src/config.rs | 17 ++++++++++++++++ src/main.rs | 54 +++++++++++++++++++++++++-------------------------- 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/src/config.rs b/src/config.rs index d93977c..431500a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,6 +5,11 @@ pub trait ConfigSource { } pub trait WalkSource { + /// Search binary files. + fn binary(&self) -> Option { + None + } + /// Skip hidden files and directories. fn ignore_hidden(&self) -> Option { None @@ -81,6 +86,7 @@ impl ConfigSource for Config { #[serde(deny_unknown_fields, default)] #[serde(rename_all = "kebab-case")] pub struct Walk { + pub binary: Option, pub ignore_hidden: Option, pub ignore_files: Option, pub ignore_dot: Option, @@ -91,6 +97,9 @@ pub struct Walk { impl Walk { pub fn update(&mut self, source: &dyn WalkSource) { + if let Some(source) = source.binary() { + self.binary = Some(source); + } if let Some(source) = source.ignore_hidden() { self.ignore_hidden = Some(source); } @@ -116,6 +125,10 @@ impl Walk { } } + pub fn binary(&self) -> bool { + self.binary.unwrap_or(false) + } + pub fn ignore_hidden(&self) -> bool { self.ignore_hidden.unwrap_or(true) } @@ -147,6 +160,10 @@ impl Walk { } impl WalkSource for Walk { + fn binary(&self) -> Option { + self.binary + } + fn ignore_hidden(&self) -> Option { self.ignore_hidden } diff --git a/src/main.rs b/src/main.rs index 684ceb2..43f3218 100644 --- a/src/main.rs +++ b/src/main.rs @@ -83,12 +83,6 @@ struct Args { )] pub format: Format, - #[structopt(long, raw(overrides_with = r#""no-binary""#))] - /// Search binary files. - binary: bool, - #[structopt(long, raw(overrides_with = r#""binary""#), raw(hidden = "true"))] - no_binary: bool, - #[structopt(flatten)] config: ConfigArgs, @@ -127,15 +121,6 @@ impl Args { (_, _) => unreachable!("StructOpt should make this impossible"), } } - - pub fn binary(&self) -> Option { - match (self.binary, self.no_binary) { - (true, false) => Some(true), - (false, true) => Some(false), - (false, false) => None, - (_, _) => unreachable!("StructOpt should make this impossible"), - } - } } #[derive(Debug, StructOpt)] @@ -154,6 +139,12 @@ impl config::ConfigSource for ConfigArgs { #[derive(Debug, StructOpt)] #[structopt(rename_all = "kebab-case")] struct WalkArgs { + #[structopt(long, raw(overrides_with = r#""no-binary""#))] + /// Search binary files. + binary: bool, + #[structopt(long, raw(overrides_with = r#""binary""#), raw(hidden = "true"))] + no_binary: bool, + #[structopt(long, raw(overrides_with = r#""no-hidden""#))] /// Search hidden files and directories. hidden: bool, @@ -200,6 +191,15 @@ struct WalkArgs { } impl config::WalkSource for WalkArgs { + fn binary(&self) -> Option { + match (self.binary, self.no_binary) { + (true, false) => Some(true), + (false, true) => Some(false), + (false, false) => None, + (_, _) => unreachable!("StructOpt should make this impossible"), + } + } + fn ignore_hidden(&self) -> Option { match (self.hidden, self.no_hidden) { (true, false) => Some(false), @@ -282,18 +282,6 @@ fn run() -> Result { let mut builder = get_logging(args.verbose.log_level()); builder.init(); - let dictionary = typos::BuiltIn::new(); - - let parser = typos::tokens::ParserBuilder::new() - .ignore_hex(args.ignore_hex().unwrap_or(true)) - .build(); - - let checks = typos::checks::CheckSettings::new() - .check_filenames(args.check_filenames().unwrap_or(true)) - .check_files(args.check_files().unwrap_or(true)) - .binary(args.binary().unwrap_or(false)) - .build(&dictionary, &parser); - let mut config = config::Config::default(); if let Some(path) = args.custom_config.as_ref() { let custom = config::Config::from_file(path)?; @@ -318,6 +306,18 @@ fn run() -> Result { config.update(&args.config); let config = config; + let dictionary = typos::BuiltIn::new(); + + let parser = typos::tokens::ParserBuilder::new() + .ignore_hex(args.ignore_hex().unwrap_or(true)) + .build(); + + let checks = typos::checks::CheckSettings::new() + .check_filenames(args.check_filenames().unwrap_or(true)) + .check_files(args.check_files().unwrap_or(true)) + .binary(config.files.binary()) + .build(&dictionary, &parser); + let mut walk = ignore::WalkBuilder::new(path); walk.hidden(config.files.ignore_hidden()) .ignore(config.files.ignore_dot()) From a2cf3b7cc9c7ce408861b657b2a12994f9c15c14 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 8 Aug 2019 08:22:46 -0500 Subject: [PATCH 16/20] feat(config): Configure checking logic Later we can add the per-filetype checks Fixes #37 --- src/config.rs | 78 +++++++++++++++++++++++++++++++- src/main.rs | 120 +++++++++++++++++++++++++++----------------------- 2 files changed, 142 insertions(+), 56 deletions(-) diff --git a/src/config.rs b/src/config.rs index 431500a..d1a6d20 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,13 @@ use std::io::Read; pub trait ConfigSource { - fn walk(&self) -> Option<&dyn WalkSource>; + fn walk(&self) -> Option<&dyn WalkSource> { + None + } + + fn default(&self) -> Option<&dyn FileSource> { + None + } } pub trait WalkSource { @@ -41,11 +47,29 @@ pub trait WalkSource { } } +pub trait FileSource { + /// Verifying spelling in file names. + fn check_filename(&self) -> Option { + None + } + + /// Verifying spelling in filess. + fn check_file(&self) -> Option { + None + } + + /// Do not check identifiers that appear to be hexadecimal values + fn ignore_hex(&self) -> Option { + None + } +} + #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields, default)] #[serde(rename_all = "kebab-case")] pub struct Config { pub files: Walk, + pub default: FileConfig, } impl Config { @@ -73,6 +97,9 @@ impl Config { if let Some(walk) = source.walk() { self.files.update(walk); } + if let Some(default) = source.default() { + self.default.update(default); + } } } @@ -189,6 +216,55 @@ impl WalkSource for Walk { } } +#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields, default)] +#[serde(rename_all = "kebab-case")] +pub struct FileConfig { + pub check_filename: Option, + pub check_file: Option, + pub ignore_hex: Option, +} + +impl FileConfig { + pub fn update(&mut self, source: &dyn FileSource) { + if let Some(source) = source.check_filename() { + self.check_filename = Some(source); + } + if let Some(source) = source.check_file() { + self.check_file = Some(source); + } + if let Some(source) = source.ignore_hex() { + self.ignore_hex = Some(source); + } + } + + pub fn check_filename(&self) -> bool { + self.check_filename.unwrap_or(true) + } + + pub fn check_file(&self) -> bool { + self.check_file.unwrap_or(true) + } + + pub fn ignore_hex(&self) -> bool { + self.ignore_hex.unwrap_or(true) + } +} + +impl FileSource for FileConfig { + fn check_filename(&self) -> Option { + self.check_filename + } + + fn check_file(&self) -> Option { + self.check_file + } + + fn ignore_hex(&self) -> Option { + self.ignore_hex + } +} + fn find_project_file(dir: std::path::PathBuf, name: &str) -> Option { let mut file_path = dir; file_path.push(name); diff --git a/src/main.rs b/src/main.rs index 43f3218..a49f32d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,31 +50,8 @@ struct Args { /// Ignore implicit configuration files. isolated: bool, - #[structopt(long, raw(overrides_with = r#""check-filenames""#))] - /// Skip verifying spelling in file names. - no_check_filenames: bool, - #[structopt( - long, - raw(overrides_with = r#""no-check-filenames""#), - raw(hidden = "true") - )] - check_filenames: bool, - - #[structopt(long, raw(overrides_with = r#""check-files""#))] - /// Skip verifying spelling in filess. - no_check_files: bool, - #[structopt( - long, - raw(overrides_with = r#""no-check-files""#), - raw(hidden = "true") - )] - check_files: bool, - - #[structopt(long, raw(overrides_with = r#""hex""#))] - /// Don't try to detect that an identifier looks like hex - no_hex: bool, - #[structopt(long, raw(overrides_with = r#""no-hex""#), raw(hidden = "true"))] - hex: bool, + #[structopt(flatten)] + overrides: FileArgs, #[structopt( long = "format", @@ -94,33 +71,6 @@ impl Args { pub fn infer(self) -> Self { self } - - pub fn check_files(&self) -> Option { - match (self.check_files, self.no_check_files) { - (true, false) => Some(true), - (false, true) => Some(false), - (false, false) => None, - (_, _) => unreachable!("StructOpt should make this impossible"), - } - } - - pub fn check_filenames(&self) -> Option { - match (self.check_filenames, self.no_check_filenames) { - (true, false) => Some(true), - (false, true) => Some(false), - (false, false) => None, - (_, _) => unreachable!("StructOpt should make this impossible"), - } - } - - pub fn ignore_hex(&self) -> Option { - match (self.no_hex, self.hex) { - (true, false) => Some(false), - (false, true) => Some(true), - (false, false) => None, - (_, _) => unreachable!("StructOpt should make this impossible"), - } - } } #[derive(Debug, StructOpt)] @@ -255,6 +205,65 @@ impl config::WalkSource for WalkArgs { } } +#[derive(Debug, StructOpt)] +#[structopt(rename_all = "kebab-case")] +pub struct FileArgs { + #[structopt(long, raw(overrides_with = r#""check-filenames""#))] + /// Skip verifying spelling in file names. + no_check_filenames: bool, + #[structopt( + long, + raw(overrides_with = r#""no-check-filenames""#), + raw(hidden = "true") + )] + check_filenames: bool, + + #[structopt(long, raw(overrides_with = r#""check-files""#))] + /// Skip verifying spelling in filess. + no_check_files: bool, + #[structopt( + long, + raw(overrides_with = r#""no-check-files""#), + raw(hidden = "true") + )] + check_files: bool, + + #[structopt(long, raw(overrides_with = r#""hex""#))] + /// Don't try to detect that an identifier looks like hex + no_hex: bool, + #[structopt(long, raw(overrides_with = r#""no-hex""#), raw(hidden = "true"))] + hex: bool, +} + +impl config::FileSource for FileArgs { + fn check_filename(&self) -> Option { + match (self.check_filenames, self.no_check_filenames) { + (true, false) => Some(true), + (false, true) => Some(false), + (false, false) => None, + (_, _) => unreachable!("StructOpt should make this impossible"), + } + } + + fn check_file(&self) -> Option { + match (self.check_files, self.no_check_files) { + (true, false) => Some(true), + (false, true) => Some(false), + (false, false) => None, + (_, _) => unreachable!("StructOpt should make this impossible"), + } + } + + fn ignore_hex(&self) -> Option { + match (self.hex, self.no_hex) { + (true, false) => Some(true), + (false, true) => Some(false), + (false, false) => None, + (_, _) => unreachable!("StructOpt should make this impossible"), + } + } +} + pub fn get_logging(level: log::Level) -> env_logger::Builder { let mut builder = env_logger::Builder::new(); @@ -304,17 +313,18 @@ fn run() -> Result { config.update(&derived); } config.update(&args.config); + config.default.update(&args.overrides); let config = config; let dictionary = typos::BuiltIn::new(); let parser = typos::tokens::ParserBuilder::new() - .ignore_hex(args.ignore_hex().unwrap_or(true)) + .ignore_hex(config.default.ignore_hex()) .build(); let checks = typos::checks::CheckSettings::new() - .check_filenames(args.check_filenames().unwrap_or(true)) - .check_files(args.check_files().unwrap_or(true)) + .check_filenames(config.default.check_filename()) + .check_files(config.default.check_file()) .binary(config.files.binary()) .build(&dictionary, &parser); From 26787df50d9de48a259e69c7f168dcb34ac18631 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 8 Aug 2019 08:28:14 -0500 Subject: [PATCH 17/20] refactor(checks): Implement traits for easier debugging --- src/checks.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/checks.rs b/src/checks.rs index 4c8b475..3941887 100644 --- a/src/checks.rs +++ b/src/checks.rs @@ -7,6 +7,7 @@ use crate::report; use crate::tokens; use crate::Dictionary; +#[derive(Debug, Clone, PartialEq, Eq)] pub struct CheckSettings { check_filenames: bool, check_files: bool, @@ -58,6 +59,7 @@ impl Default for CheckSettings { } } +#[derive(Clone)] pub struct Checks<'d, 'p> { dictionary: &'d Dictionary, parser: &'p tokens::Parser, @@ -170,3 +172,14 @@ impl<'d, 'p> Checks<'d, 'p> { Ok(typos_found) } } + +impl std::fmt::Debug for Checks<'_, '_> { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.debug_struct("Checks") + .field("parser", self.parser) + .field("check_filenames", &self.check_filenames) + .field("check_files", &self.check_files) + .field("binary", &self.binary) + .finish() + } +} From 8ea31b5e1d5f99f7f7c77a1644a06ee48214232a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 8 Aug 2019 08:31:50 -0500 Subject: [PATCH 18/20] refactor(cli): Re-order code to make diffing easier --- src/main.rs | 118 ++++++++++++++++++++++++++-------------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/src/main.rs b/src/main.rs index a49f32d..7a7129c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -73,6 +73,65 @@ impl Args { } } +#[derive(Debug, StructOpt)] +#[structopt(rename_all = "kebab-case")] +pub struct FileArgs { + #[structopt(long, raw(overrides_with = r#""check-filenames""#))] + /// Skip verifying spelling in file names. + no_check_filenames: bool, + #[structopt( + long, + raw(overrides_with = r#""no-check-filenames""#), + raw(hidden = "true") + )] + check_filenames: bool, + + #[structopt(long, raw(overrides_with = r#""check-files""#))] + /// Skip verifying spelling in filess. + no_check_files: bool, + #[structopt( + long, + raw(overrides_with = r#""no-check-files""#), + raw(hidden = "true") + )] + check_files: bool, + + #[structopt(long, raw(overrides_with = r#""hex""#))] + /// Don't try to detect that an identifier looks like hex + no_hex: bool, + #[structopt(long, raw(overrides_with = r#""no-hex""#), raw(hidden = "true"))] + hex: bool, +} + +impl config::FileSource for FileArgs { + fn check_filename(&self) -> Option { + match (self.check_filenames, self.no_check_filenames) { + (true, false) => Some(true), + (false, true) => Some(false), + (false, false) => None, + (_, _) => unreachable!("StructOpt should make this impossible"), + } + } + + fn check_file(&self) -> Option { + match (self.check_files, self.no_check_files) { + (true, false) => Some(true), + (false, true) => Some(false), + (false, false) => None, + (_, _) => unreachable!("StructOpt should make this impossible"), + } + } + + fn ignore_hex(&self) -> Option { + match (self.hex, self.no_hex) { + (true, false) => Some(true), + (false, true) => Some(false), + (false, false) => None, + (_, _) => unreachable!("StructOpt should make this impossible"), + } + } +} + #[derive(Debug, StructOpt)] #[structopt(rename_all = "kebab-case")] struct ConfigArgs { @@ -205,65 +264,6 @@ impl config::WalkSource for WalkArgs { } } -#[derive(Debug, StructOpt)] -#[structopt(rename_all = "kebab-case")] -pub struct FileArgs { - #[structopt(long, raw(overrides_with = r#""check-filenames""#))] - /// Skip verifying spelling in file names. - no_check_filenames: bool, - #[structopt( - long, - raw(overrides_with = r#""no-check-filenames""#), - raw(hidden = "true") - )] - check_filenames: bool, - - #[structopt(long, raw(overrides_with = r#""check-files""#))] - /// Skip verifying spelling in filess. - no_check_files: bool, - #[structopt( - long, - raw(overrides_with = r#""no-check-files""#), - raw(hidden = "true") - )] - check_files: bool, - - #[structopt(long, raw(overrides_with = r#""hex""#))] - /// Don't try to detect that an identifier looks like hex - no_hex: bool, - #[structopt(long, raw(overrides_with = r#""no-hex""#), raw(hidden = "true"))] - hex: bool, -} - -impl config::FileSource for FileArgs { - fn check_filename(&self) -> Option { - match (self.check_filenames, self.no_check_filenames) { - (true, false) => Some(true), - (false, true) => Some(false), - (false, false) => None, - (_, _) => unreachable!("StructOpt should make this impossible"), - } - } - - fn check_file(&self) -> Option { - match (self.check_files, self.no_check_files) { - (true, false) => Some(true), - (false, true) => Some(false), - (false, false) => None, - (_, _) => unreachable!("StructOpt should make this impossible"), - } - } - - fn ignore_hex(&self) -> Option { - match (self.hex, self.no_hex) { - (true, false) => Some(true), - (false, true) => Some(false), - (false, false) => None, - (_, _) => unreachable!("StructOpt should make this impossible"), - } - } -} - pub fn get_logging(level: log::Level) -> env_logger::Builder { let mut builder = env_logger::Builder::new(); From 709446821bfe53baf426be66878f966544c91442 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 8 Aug 2019 08:32:17 -0500 Subject: [PATCH 19/20] refactor(cli): Remove dead code --- src/main.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7a7129c..e655e7b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -67,12 +67,6 @@ struct Args { verbose: clap_verbosity_flag::Verbosity, } -impl Args { - pub fn infer(self) -> Self { - self - } -} - #[derive(Debug, StructOpt)] #[structopt(rename_all = "kebab-case")] pub struct FileArgs { @@ -286,7 +280,7 @@ pub fn get_logging(level: log::Level) -> env_logger::Builder { } fn run() -> Result { - let args = Args::from_args().infer(); + let args = Args::from_args(); let mut builder = get_logging(args.verbose.log_level()); builder.init(); From 6fc61966cc8cde319df4f8ee61c676d3a90922d8 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 8 Aug 2019 08:37:06 -0500 Subject: [PATCH 20/20] feat(parser): Give control over identifier detection --- src/config.rs | 37 +++++++++++++++++++++++++++++++++++++ src/main.rs | 2 ++ 2 files changed, 39 insertions(+) diff --git a/src/config.rs b/src/config.rs index d1a6d20..80903f6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -62,6 +62,16 @@ pub trait FileSource { fn ignore_hex(&self) -> Option { None } + + /// Allow identifiers to include digits, in addition to letters + fn identifier_include_digits(&self) -> Option { + None + } + + /// Specify additional characters to be included in identifiers + fn identifier_include_chars(&self) -> Option<&str> { + None + } } #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] @@ -223,6 +233,8 @@ pub struct FileConfig { pub check_filename: Option, pub check_file: Option, pub ignore_hex: Option, + pub identifier_include_digits: Option, + pub identifier_include_chars: Option, } impl FileConfig { @@ -236,6 +248,12 @@ impl FileConfig { if let Some(source) = source.ignore_hex() { self.ignore_hex = Some(source); } + if let Some(source) = source.identifier_include_digits() { + self.identifier_include_digits = Some(source); + } + if let Some(source) = source.identifier_include_chars() { + self.identifier_include_chars = Some(source.to_owned()); + } } pub fn check_filename(&self) -> bool { @@ -249,6 +267,17 @@ impl FileConfig { pub fn ignore_hex(&self) -> bool { self.ignore_hex.unwrap_or(true) } + + pub fn identifier_include_digits(&self) -> bool { + self.identifier_include_digits.unwrap_or(true) + } + + pub fn identifier_include_chars(&self) -> &str { + self.identifier_include_chars + .as_ref() + .map(|s| s.as_str()) + .unwrap_or("_'") + } } impl FileSource for FileConfig { @@ -263,6 +292,14 @@ impl FileSource for FileConfig { fn ignore_hex(&self) -> Option { self.ignore_hex } + + fn identifier_include_digits(&self) -> Option { + self.identifier_include_digits + } + + fn identifier_include_chars(&self) -> Option<&str> { + self.identifier_include_chars.as_ref().map(|s| s.as_str()) + } } fn find_project_file(dir: std::path::PathBuf, name: &str) -> Option { diff --git a/src/main.rs b/src/main.rs index e655e7b..319858c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -314,6 +314,8 @@ fn run() -> Result { let parser = typos::tokens::ParserBuilder::new() .ignore_hex(config.default.ignore_hex()) + .include_digits(config.default.identifier_include_digits()) + .include_chars(config.default.identifier_include_chars().to_owned()) .build(); let checks = typos::checks::CheckSettings::new()