mirror of
https://github.com/crate-ci/typos.git
synced 2024-11-26 02:51:08 -05:00
commit
cb02353b5a
6 changed files with 314 additions and 20 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -767,6 +767,12 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "maplit"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.3.4"
|
version = "2.3.4"
|
||||||
|
@ -1516,6 +1522,7 @@ dependencies = [
|
||||||
"itertools 0.10.0",
|
"itertools 0.10.0",
|
||||||
"kstring",
|
"kstring",
|
||||||
"log",
|
"log",
|
||||||
|
"maplit",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"phf",
|
"phf",
|
||||||
"predicates",
|
"predicates",
|
||||||
|
|
|
@ -65,6 +65,7 @@ typed-arena = "2.0.1"
|
||||||
assert_fs = "1.0"
|
assert_fs = "1.0"
|
||||||
predicates = "1.0"
|
predicates = "1.0"
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
|
maplit = "1.0"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
|
@ -39,7 +39,7 @@ See also [benchmarks](../benchsuite/runs).
|
||||||
| Runtime | \- | \- | \- | Python | Python |
|
| Runtime | \- | \- | \- | Python | Python |
|
||||||
| Dictionary | Blacklist | Blacklist | Blacklist | Blacklist | Whitelist |
|
| Dictionary | Blacklist | Blacklist | Blacklist | Blacklist | Whitelist |
|
||||||
| Custom Dict | Yes | No | ? | Yes | Yes |
|
| Custom Dict | Yes | No | ? | Yes | Yes |
|
||||||
| Per-Lang Dict | No ([#14][def-14]) | No | ? | No | Yes |
|
| Per-Lang Dict | Yes | No | ? | No | Yes |
|
||||||
| CamelCase | Yes | No | ? | No | Yes |
|
| CamelCase | Yes | No | ? | No | Yes |
|
||||||
| snake_case | Yes | No | ? | No | Yes |
|
| snake_case | Yes | No | ? | No | Yes |
|
||||||
| Ignore Hex | Yes | No | ? | No | Yes |
|
| Ignore Hex | Yes | No | ? | No | Yes |
|
||||||
|
|
|
@ -31,4 +31,5 @@ Configuration is read from the following (in precedence order)
|
||||||
| default.locale | --locale | en, en-us, en-gb, en-ca, en-au | English dialect to correct to. |
|
| default.locale | --locale | en, en-us, en-gb, en-ca, en-au | English dialect to correct to. |
|
||||||
| default.extend-identifiers | \- | table of strings | Corrections for identifiers. When the correction is blank, the word is never valid. When the correction is the key, the word is always valid. |
|
| default.extend-identifiers | \- | table of strings | Corrections for identifiers. When the correction is blank, the word is never valid. When the correction is the key, the word is always valid. |
|
||||||
| default.extend-words | \- | table of strings | Corrections for identifiers. When the correction is blank, the word is never valid. When the correction is the key, the word is always valid. |
|
| default.extend-words | \- | table of strings | Corrections for identifiers. When the correction is blank, the word is never valid. When the correction is the key, the word is always valid. |
|
||||||
| type.<name>.binary | <varied> | <varied> | See `default.` for child keys. Run with `--type-list` to see available `<name>`s |
|
| type.<name>.<field> | <varied> | <varied> | See `default.` for child keys. Run with `--type-list` to see available `<name>`s |
|
||||||
|
| type.<name>.extend_globs | \- | list of strings | File globs for matching `<name>` |
|
||||||
|
|
107
src/config.rs
107
src/config.rs
|
@ -1,13 +1,13 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(deny_unknown_fields, default)]
|
#[serde(deny_unknown_fields, default)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub files: Walk,
|
pub files: Walk,
|
||||||
pub default: EngineConfig,
|
pub default: EngineConfig,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub type_: std::collections::HashMap<kstring::KString, EngineConfig>,
|
pub type_: std::collections::HashMap<kstring::KString, TypeEngineConfig>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub overrides: EngineConfig,
|
pub overrides: EngineConfig,
|
||||||
}
|
}
|
||||||
|
@ -46,10 +46,17 @@ impl Config {
|
||||||
pub fn update(&mut self, source: &Config) {
|
pub fn update(&mut self, source: &Config) {
|
||||||
self.files.update(&source.files);
|
self.files.update(&source.files);
|
||||||
self.default.update(&source.default);
|
self.default.update(&source.default);
|
||||||
|
for (type_name, engine) in source.type_.iter() {
|
||||||
|
self.type_
|
||||||
|
.entry(type_name.to_owned())
|
||||||
|
.or_insert_with(TypeEngineConfig::default)
|
||||||
|
.update(engine);
|
||||||
|
}
|
||||||
|
self.overrides.update(&source.overrides);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(deny_unknown_fields, default)]
|
#[serde(deny_unknown_fields, default)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Walk {
|
pub struct Walk {
|
||||||
|
@ -130,7 +137,23 @@ impl Walk {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields, default)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
pub struct TypeEngineConfig {
|
||||||
|
pub extend_glob: Vec<kstring::KString>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub engine: EngineConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeEngineConfig {
|
||||||
|
pub fn update(&mut self, source: &TypeEngineConfig) {
|
||||||
|
self.extend_glob.extend(source.extend_glob.iter().cloned());
|
||||||
|
self.engine.update(&source.engine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(deny_unknown_fields, default)]
|
#[serde(deny_unknown_fields, default)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct EngineConfig {
|
pub struct EngineConfig {
|
||||||
|
@ -203,7 +226,7 @@ impl EngineConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(deny_unknown_fields, default)]
|
#[serde(deny_unknown_fields, default)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct TokenizerConfig {
|
pub struct TokenizerConfig {
|
||||||
|
@ -274,7 +297,7 @@ impl TokenizerConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(deny_unknown_fields, default)]
|
#[serde(deny_unknown_fields, default)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct DictConfig {
|
pub struct DictConfig {
|
||||||
|
@ -343,7 +366,7 @@ fn find_project_file(dir: &std::path::Path, names: &[&str]) -> Option<std::path:
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum Locale {
|
pub enum Locale {
|
||||||
En,
|
En,
|
||||||
|
@ -401,3 +424,73 @@ impl std::fmt::Display for Locale {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_defaults() {
|
||||||
|
let null = Config::default();
|
||||||
|
let defaulted = Config::from_defaults();
|
||||||
|
assert_ne!(defaulted, null);
|
||||||
|
assert_ne!(defaulted.files, null.files);
|
||||||
|
assert_ne!(defaulted.default, null.default);
|
||||||
|
assert_ne!(defaulted.default.tokenizer, null.default.tokenizer);
|
||||||
|
assert_ne!(defaulted.default.dict, null.default.dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update_from_nothing() {
|
||||||
|
let null = Config::default();
|
||||||
|
let defaulted = Config::from_defaults();
|
||||||
|
|
||||||
|
let mut actual = defaulted.clone();
|
||||||
|
actual.update(&null);
|
||||||
|
|
||||||
|
assert_eq!(actual, defaulted);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update_from_defaults() {
|
||||||
|
let null = Config::default();
|
||||||
|
let defaulted = Config::from_defaults();
|
||||||
|
|
||||||
|
let mut actual = null;
|
||||||
|
actual.update(&defaulted);
|
||||||
|
|
||||||
|
assert_eq!(actual, defaulted);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extend_glob_updates() {
|
||||||
|
let null = TypeEngineConfig::default();
|
||||||
|
let extended = TypeEngineConfig {
|
||||||
|
extend_glob: vec!["*.foo".into()],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut actual = null;
|
||||||
|
actual.update(&extended);
|
||||||
|
|
||||||
|
assert_eq!(actual, extended);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extend_glob_extends() {
|
||||||
|
let base = TypeEngineConfig {
|
||||||
|
extend_glob: vec!["*.foo".into()],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let extended = TypeEngineConfig {
|
||||||
|
extend_glob: vec!["*.bar".into()],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut actual = base;
|
||||||
|
actual.update(&extended);
|
||||||
|
|
||||||
|
let expected: Vec<kstring::KString> = vec!["*.foo".into(), "*.bar".into()];
|
||||||
|
assert_eq!(actual.extend_glob, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
214
src/policy.rs
214
src/policy.rs
|
@ -131,6 +131,25 @@ impl<'s> ConfigEngine<'s> {
|
||||||
config.update(overrides);
|
config.update(overrides);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut types = Default::default();
|
||||||
|
std::mem::swap(&mut types, &mut config.type_);
|
||||||
|
let mut types = types
|
||||||
|
.into_iter()
|
||||||
|
.map(|(type_, type_engine)| {
|
||||||
|
let mut new_engine = config.default.clone();
|
||||||
|
new_engine.update(&type_engine.engine);
|
||||||
|
new_engine.update(&config.overrides);
|
||||||
|
let new_type_engine = crate::config::TypeEngineConfig {
|
||||||
|
extend_glob: type_engine.extend_glob,
|
||||||
|
engine: new_engine,
|
||||||
|
};
|
||||||
|
(type_, new_type_engine)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
std::mem::swap(&mut types, &mut config.type_);
|
||||||
|
|
||||||
|
config.default.update(&config.overrides);
|
||||||
|
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,24 +168,37 @@ impl<'s> ConfigEngine<'s> {
|
||||||
|
|
||||||
let walk = self.walk.intern(files);
|
let walk = self.walk.intern(files);
|
||||||
|
|
||||||
let types = type_
|
let mut type_matcher = ignore::types::TypesBuilder::new();
|
||||||
.into_iter()
|
type_matcher.add_defaults();
|
||||||
.map(|(type_, type_engine)| {
|
let mut types: std::collections::HashMap<_, _> = Default::default();
|
||||||
let mut new_type_engine = default.clone();
|
for (type_name, type_engine) in type_.into_iter() {
|
||||||
new_type_engine.update(&type_engine);
|
if type_engine.extend_glob.is_empty() {
|
||||||
new_type_engine.update(&overrides);
|
if type_matcher
|
||||||
let type_config = self.init_file_config(new_type_engine);
|
.definitions()
|
||||||
(type_, type_config)
|
.iter()
|
||||||
})
|
.all(|def| def.name() != type_name.as_str())
|
||||||
.collect();
|
{
|
||||||
|
anyhow::bail!("Unknown type definition `{}`, pass `--type-list` to see valid names or set `extend_glob` to add a new one.", type_name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for glob in type_engine.extend_glob.iter() {
|
||||||
|
type_matcher.add(type_name.as_str(), glob.as_str())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let type_config = self.init_file_config(type_engine.engine);
|
||||||
|
types.insert(type_name, type_config);
|
||||||
|
}
|
||||||
default.update(&overrides);
|
default.update(&overrides);
|
||||||
let default = self.init_file_config(default);
|
let default = self.init_file_config(default);
|
||||||
|
|
||||||
|
type_matcher.select("all");
|
||||||
|
|
||||||
let dir = DirConfig {
|
let dir = DirConfig {
|
||||||
walk,
|
walk,
|
||||||
default,
|
default,
|
||||||
types,
|
types,
|
||||||
type_matcher: ignore::types::TypesBuilder::new().add_defaults().build()?,
|
type_matcher: type_matcher.build()?,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.configs.insert(cwd.to_owned(), dir);
|
self.configs.insert(cwd.to_owned(), dir);
|
||||||
|
@ -307,3 +339,163 @@ impl<'t, 'd> Default for Policy<'t, 'd> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const NEVER_EXIST_TYPE: &str = "THISyTYPEySHOULDyNEVERyEXISTyBUTyIyHATEyYOUyIFyITyDOES";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_load_config_applies_overrides() {
|
||||||
|
let storage = ConfigStorage::new();
|
||||||
|
let mut engine = ConfigEngine::new(&storage);
|
||||||
|
engine.set_isolated(true);
|
||||||
|
|
||||||
|
let type_name = kstring::KString::from_static("toml");
|
||||||
|
|
||||||
|
let config = crate::config::Config {
|
||||||
|
default: crate::config::EngineConfig {
|
||||||
|
binary: Some(true),
|
||||||
|
check_filename: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
type_: maplit::hashmap! {
|
||||||
|
type_name.clone() => crate::config::TypeEngineConfig {
|
||||||
|
engine: crate::config::EngineConfig {
|
||||||
|
check_filename: Some(false),
|
||||||
|
check_file: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
overrides: crate::config::EngineConfig {
|
||||||
|
binary: Some(false),
|
||||||
|
check_file: Some(false),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
engine.set_overrides(config);
|
||||||
|
|
||||||
|
let cwd = std::path::Path::new(".");
|
||||||
|
let loaded = engine.load_config(&cwd).unwrap();
|
||||||
|
assert_eq!(loaded.default.binary, Some(false));
|
||||||
|
assert_eq!(loaded.default.check_filename, Some(true));
|
||||||
|
assert_eq!(loaded.default.check_file, Some(false));
|
||||||
|
assert_eq!(loaded.type_[type_name.as_str()].engine.binary, Some(false));
|
||||||
|
assert_eq!(
|
||||||
|
loaded.type_[type_name.as_str()].engine.check_filename,
|
||||||
|
Some(false)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
loaded.type_[type_name.as_str()].engine.check_file,
|
||||||
|
Some(false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_init_fails_on_unknown_type() {
|
||||||
|
let storage = ConfigStorage::new();
|
||||||
|
let mut engine = ConfigEngine::new(&storage);
|
||||||
|
engine.set_isolated(true);
|
||||||
|
|
||||||
|
let type_name = kstring::KString::from_static(NEVER_EXIST_TYPE);
|
||||||
|
|
||||||
|
let config = crate::config::Config {
|
||||||
|
type_: maplit::hashmap! {
|
||||||
|
type_name => crate::config::TypeEngineConfig {
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
engine.set_overrides(config);
|
||||||
|
|
||||||
|
let cwd = std::path::Path::new(".");
|
||||||
|
let result = engine.init_dir(&cwd);
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_policy_default() {
|
||||||
|
let storage = ConfigStorage::new();
|
||||||
|
let mut engine = ConfigEngine::new(&storage);
|
||||||
|
engine.set_isolated(true);
|
||||||
|
|
||||||
|
let config = crate::config::Config::default();
|
||||||
|
engine.set_overrides(config);
|
||||||
|
|
||||||
|
let cwd = std::path::Path::new(".");
|
||||||
|
engine.init_dir(&cwd).unwrap();
|
||||||
|
let policy = engine.policy(&cwd.join("Cargo.toml"));
|
||||||
|
assert!(!policy.binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_policy_fallback() {
|
||||||
|
let storage = ConfigStorage::new();
|
||||||
|
let mut engine = ConfigEngine::new(&storage);
|
||||||
|
engine.set_isolated(true);
|
||||||
|
|
||||||
|
let type_name = kstring::KString::from_static(NEVER_EXIST_TYPE);
|
||||||
|
|
||||||
|
let config = crate::config::Config {
|
||||||
|
default: crate::config::EngineConfig {
|
||||||
|
binary: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
type_: maplit::hashmap! {
|
||||||
|
type_name.clone() => crate::config::TypeEngineConfig {
|
||||||
|
extend_glob: vec![type_name],
|
||||||
|
engine: crate::config::EngineConfig {
|
||||||
|
binary: Some(false),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
engine.set_overrides(config);
|
||||||
|
|
||||||
|
let cwd = std::path::Path::new(".");
|
||||||
|
engine.init_dir(&cwd).unwrap();
|
||||||
|
let policy = engine.policy(&cwd.join("Cargo.toml"));
|
||||||
|
assert!(policy.binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_policy_type_specific() {
|
||||||
|
let storage = ConfigStorage::new();
|
||||||
|
let mut engine = ConfigEngine::new(&storage);
|
||||||
|
engine.set_isolated(true);
|
||||||
|
|
||||||
|
let type_name = kstring::KString::from_static(NEVER_EXIST_TYPE);
|
||||||
|
|
||||||
|
let config = crate::config::Config {
|
||||||
|
default: crate::config::EngineConfig {
|
||||||
|
binary: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
type_: maplit::hashmap! {
|
||||||
|
type_name.clone() => crate::config::TypeEngineConfig {
|
||||||
|
extend_glob: vec![type_name],
|
||||||
|
engine: crate::config::EngineConfig {
|
||||||
|
binary: Some(false),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
engine.set_overrides(config);
|
||||||
|
|
||||||
|
let cwd = std::path::Path::new(".");
|
||||||
|
engine.init_dir(&cwd).unwrap();
|
||||||
|
let policy = engine.policy(&cwd.join("Cargo.toml"));
|
||||||
|
assert!(policy.binary);
|
||||||
|
let policy = engine.policy(&cwd.join(NEVER_EXIST_TYPE));
|
||||||
|
assert!(!policy.binary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue