diff --git a/Cargo.lock b/Cargo.lock index 5fac76523..cec2e9613 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1685,6 +1685,7 @@ dependencies = [ "serde", "serde_json", "serde_regex", + "snapbox", "thread_local", "toml", "trycmd", diff --git a/crates/typos-cli/Cargo.toml b/crates/typos-cli/Cargo.toml index d734ff578..f10187b07 100644 --- a/crates/typos-cli/Cargo.toml +++ b/crates/typos-cli/Cargo.toml @@ -86,6 +86,7 @@ encoding_rs = "0.8.32" assert_fs = "1.0" trycmd = "0.14.16" criterion = "0.5" +snapbox = "0.4.11" [[bench]] name = "checks" diff --git a/crates/typos-cli/src/config.rs b/crates/typos-cli/src/config.rs index b1eb7c722..5eb5d5c88 100644 --- a/crates/typos-cli/src/config.rs +++ b/crates/typos-cli/src/config.rs @@ -2,6 +2,8 @@ use std::collections::HashMap; use kstring::KString; +use crate::file_type_specifics; + pub const SUPPORTED_FILE_NAMES: &[&str] = &["typos.toml", "_typos.toml", ".typos.toml"]; #[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] @@ -155,9 +157,11 @@ pub struct TypeEngineConfig { impl TypeEngineConfig { pub fn from_defaults() -> Self { - let patterns = [ - ( - KString::from("lock"), + let mut patterns = HashMap::new(); + + for no_check_type in file_type_specifics::NO_CHECK_TYPES { + patterns.insert( + KString::from(*no_check_type), GlobEngineConfig { extend_glob: Vec::new(), engine: EngineConfig { @@ -165,83 +169,34 @@ impl TypeEngineConfig { ..Default::default() }, }, - ), - ( - KString::from("vim"), - GlobEngineConfig { - extend_glob: Vec::new(), - engine: EngineConfig { - dict: Some(DictConfig { - extend_identifiers: maplit::hashmap! { - "windo".into() => "windo".into(), - }, - ..Default::default() - }), - ..Default::default() - }, - }, - ), - ( - KString::from("vimscript"), - GlobEngineConfig { - extend_glob: Vec::new(), - engine: EngineConfig { - dict: Some(DictConfig { - extend_identifiers: maplit::hashmap! { - "windo".into() => "windo".into(), - }, - ..Default::default() - }), - ..Default::default() - }, - }, - ), - ( - KString::from("rust"), - GlobEngineConfig { - extend_glob: Vec::new(), - engine: EngineConfig { - dict: Some(DictConfig { - extend_identifiers: maplit::hashmap! { - "flate2".into() => "flate2".into(), - }, - extend_words: maplit::hashmap! { - "ser".into() => "ser".into(), - }, - ..Default::default() - }), - ..Default::default() - }, - }, - ), - ( - KString::from("py"), + ); + } + + for (typ, dict_config) in file_type_specifics::TYPE_SPECIFIC_DICTS { + patterns.insert( + KString::from(*typ), GlobEngineConfig { extend_glob: Vec::new(), engine: EngineConfig { dict: Some(DictConfig { - extend_identifiers: maplit::hashmap! { - "NDArray".into() => "NDArray".into(), - }, + extend_identifiers: dict_config + .ignore_idents + .iter() + .map(|key| ((*key).into(), (*key).into())) + .collect(), + extend_words: dict_config + .ignore_words + .iter() + .map(|key| ((*key).into(), (*key).into())) + .collect(), ..Default::default() }), ..Default::default() }, }, - ), - ( - KString::from("cert"), - GlobEngineConfig { - extend_glob: Vec::new(), - engine: EngineConfig { - check_file: Some(false), - ..Default::default() - }, - }, - ), - ] - .into_iter() - .collect(); + ); + } + Self { patterns } } diff --git a/crates/typos-cli/src/file_type_specifics.rs b/crates/typos-cli/src/file_type_specifics.rs new file mode 100644 index 000000000..76442cc78 --- /dev/null +++ b/crates/typos-cli/src/file_type_specifics.rs @@ -0,0 +1,80 @@ +//! This module specifies [`EngineConfig`] defaults for the file types defined in [`default_types`]. +//! +//! [`EngineConfig`]: crate::config::EngineConfig +//! [`default_types`]: crate::default_types + +/// Set `check_file` to `false` for these types. +pub const NO_CHECK_TYPES: &[&str] = &["cert", "lock"]; + +pub const TYPE_SPECIFIC_DICTS: &[(&str, StaticDictConfig)] = &[ + ( + "py", + StaticDictConfig { + ignore_idents: &[ + "NDArray", // numpy.typing.NDArray + ], + ignore_words: &[], + }, + ), + ( + "rust", + StaticDictConfig { + ignore_idents: &[ + "flate2", // https://crates.io/crates/flate2 + ], + ignore_words: &[ + "ser", // serde::ser, serde_json::ser, etc. + ], + }, + ), + ( + "vim", + StaticDictConfig { + ignore_idents: &[ + "windo", // https://vimdoc.sourceforge.net/htmldoc/windows.html#:windo + ], + ignore_words: &[], + }, + ), + ( + "vimscript", + StaticDictConfig { + ignore_idents: &[ + "windo", // https://vimdoc.sourceforge.net/htmldoc/windows.html#:windo + ], + ignore_words: &[], + }, + ), +]; + +pub struct StaticDictConfig { + pub ignore_idents: &'static [&'static str], + pub ignore_words: &'static [&'static str], +} + +#[cfg(test)] +mod tests { + use itertools::Itertools; + + use super::TYPE_SPECIFIC_DICTS; + + #[test] + fn test_type_specific_dicts_contains_no_duplicates() { + let types: Vec<_> = TYPE_SPECIFIC_DICTS.iter().map(|(typ, _)| *typ).collect(); + let types_unique: Vec<_> = types.clone().into_iter().unique().collect(); + + snapbox::assert_eq(types.join("\n"), types_unique.join("\n")); + } + + #[test] + fn test_type_specific_dicts_is_sorted() { + // The order of the entries in TYPE_SPECIFIC_DICTS actually doesn't + // affect the runtime behavior, we just want them ordered + // so that it's easier to find entries for contributors. + + let types: Vec<_> = TYPE_SPECIFIC_DICTS.iter().map(|(typ, _)| *typ).collect(); + let types_sorted: Vec<_> = types.iter().cloned().sorted().collect(); + + snapbox::assert_eq(types.join("\n"), types_sorted.join("\n")); + } +} diff --git a/crates/typos-cli/src/lib.rs b/crates/typos-cli/src/lib.rs index 4437e2d5d..40fe34873 100644 --- a/crates/typos-cli/src/lib.rs +++ b/crates/typos-cli/src/lib.rs @@ -14,3 +14,4 @@ pub mod report; mod default_types; mod file_type; +mod file_type_specifics;