diff --git a/docs/reference.md b/docs/reference.md index 32039da..0cfabf5 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -31,3 +31,4 @@ 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.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. | +| type..binary | | | See `default.` for child keys. Run with `--type-list` to see available ``s | diff --git a/src/args.rs b/src/args.rs index 16e2db7..aa8bf00 100644 --- a/src/args.rs +++ b/src/args.rs @@ -79,8 +79,9 @@ pub(crate) struct Args { /// Write the current configuration to file with `-` for stdout pub(crate) dump_config: Option, - #[structopt(flatten)] - pub(crate) overrides: FileArgs, + #[structopt(long, group = "mode")] + /// Show all supported file types. + pub(crate) type_list: bool, #[structopt( long, @@ -129,7 +130,20 @@ pub(crate) struct FileArgs { pub(crate) locale: Option, } -impl config::EngineSource for FileArgs { +impl FileArgs { + pub fn to_config(&self) -> config::EngineConfig { + config::EngineConfig { + binary: self.binary(), + check_filename: self.check_filename(), + check_file: self.check_file(), + tokenizer: None, + dict: Some(config::DictConfig { + locale: self.locale, + ..Default::default() + }), + } + } + fn binary(&self) -> Option { match (self.binary, self.no_binary) { (true, false) => Some(true), @@ -156,16 +170,6 @@ impl config::EngineSource for FileArgs { (_, _) => unreachable!("StructOpt should make this impossible"), } } - - fn dict(&self) -> Option<&dyn config::DictSource> { - Some(self) - } -} - -impl config::DictSource for FileArgs { - fn locale(&self) -> Option { - self.locale - } } #[derive(Debug, StructOpt)] @@ -173,11 +177,17 @@ impl config::DictSource for FileArgs { pub(crate) struct ConfigArgs { #[structopt(flatten)] walk: WalkArgs, + #[structopt(flatten)] + overrides: FileArgs, } -impl config::ConfigSource for ConfigArgs { - fn walk(&self) -> Option<&dyn config::WalkSource> { - Some(&self.walk) +impl ConfigArgs { + pub fn to_config(&self) -> config::Config { + config::Config { + files: self.walk.to_config(), + overrides: self.overrides.to_config(), + ..Default::default() + } } } @@ -221,7 +231,18 @@ pub(crate) struct WalkArgs { ignore_vcs: bool, } -impl config::WalkSource for WalkArgs { +impl WalkArgs { + pub fn to_config(&self) -> config::Walk { + config::Walk { + ignore_hidden: self.ignore_hidden(), + ignore_files: self.ignore_files(), + ignore_dot: self.ignore_dot(), + ignore_vcs: self.ignore_vcs(), + ignore_global: self.ignore_global(), + ignore_parent: self.ignore_parent(), + } + } + fn ignore_hidden(&self) -> Option { match (self.hidden, self.no_hidden) { (true, false) => Some(false), diff --git a/src/config.rs b/src/config.rs index 97cb9d5..e0c6a10 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,119 +1,15 @@ use std::collections::HashMap; -pub trait ConfigSource { - fn walk(&self) -> Option<&dyn WalkSource> { - None - } - - fn default(&self) -> Option<&dyn EngineSource> { - None - } -} - -pub trait WalkSource { - /// 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 - } -} - -pub trait EngineSource { - /// Check binary files. - fn binary(&self) -> Option { - None - } - - /// Verifying spelling in file names. - fn check_filename(&self) -> Option { - None - } - - /// Verifying spelling in files. - fn check_file(&self) -> Option { - None - } - - fn tokenizer(&self) -> Option<&dyn TokenizerSource> { - None - } - - fn dict(&self) -> Option<&dyn DictSource> { - None - } -} - -pub trait TokenizerSource { - /// Do not check identifiers that appear to be hexadecimal values. - fn ignore_hex(&self) -> Option { - None - } - - /// Allow identifiers to start with digits, in addition to letters. - fn identifier_leading_digits(&self) -> Option { - None - } - - /// Allow identifiers to start with one of these characters. - fn identifier_leading_chars(&self) -> Option<&str> { - None - } - - /// Allow identifiers to include digits, in addition to letters. - fn identifier_include_digits(&self) -> Option { - None - } - - /// Allow identifiers to include these characters. - fn identifier_include_chars(&self) -> Option<&str> { - None - } -} - -pub trait DictSource { - fn locale(&self) -> Option { - None - } - - fn extend_identifiers(&self) -> Box + '_> { - Box::new(None.into_iter()) - } - - fn extend_words(&self) -> Box + '_> { - Box::new(None.into_iter()) - } -} - #[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: EngineConfig, + #[serde(rename = "type")] + pub type_: std::collections::HashMap, + #[serde(skip)] + pub overrides: EngineConfig, } impl Config { @@ -142,26 +38,14 @@ impl Config { Self { files: Walk::from_defaults(), default: EngineConfig::from_defaults(), + type_: Default::default(), + overrides: EngineConfig::default(), } } - pub fn update(&mut self, source: &dyn ConfigSource) { - if let Some(walk) = source.walk() { - self.files.update(walk); - } - if let Some(default) = source.default() { - self.default.update(default); - } - } -} - -impl ConfigSource for Config { - fn walk(&self) -> Option<&dyn WalkSource> { - Some(&self.files) - } - - fn default(&self) -> Option<&dyn EngineSource> { - Some(&self.default) + pub fn update(&mut self, source: &Config) { + self.files.update(&source.files); + self.default.update(&source.default); } } @@ -169,11 +53,17 @@ impl ConfigSource for Config { #[serde(deny_unknown_fields, default)] #[serde(rename_all = "kebab-case")] pub struct Walk { + /// Skip hidden files and directories. pub ignore_hidden: Option, + /// Respect ignore files. pub ignore_files: Option, + /// Respect .ignore files. pub ignore_dot: Option, + /// Respect ignore files in vcs directories. pub ignore_vcs: Option, + /// Respect global ignore files. pub ignore_global: Option, + /// Respect ignore files in parent directories. pub ignore_parent: Option, } @@ -190,28 +80,28 @@ impl Walk { } } - pub fn update(&mut self, source: &dyn WalkSource) { - if let Some(source) = source.ignore_hidden() { + pub fn update(&mut self, source: &Walk) { + if let Some(source) = source.ignore_hidden { self.ignore_hidden = Some(source); } - if let Some(source) = source.ignore_files() { + 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() { + if let Some(source) = source.ignore_dot { self.ignore_dot = Some(source); } - if let Some(source) = source.ignore_vcs() { + if let Some(source) = source.ignore_vcs { self.ignore_vcs = Some(source); self.ignore_global = None; } - if let Some(source) = source.ignore_global() { + if let Some(source) = source.ignore_global { self.ignore_global = Some(source); } - if let Some(source) = source.ignore_parent() { + if let Some(source) = source.ignore_parent { self.ignore_parent = Some(source); } } @@ -240,38 +130,15 @@ impl Walk { } } -impl WalkSource for Walk { - 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 - } -} - #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields, default)] #[serde(rename_all = "kebab-case")] pub struct EngineConfig { + /// Check binary files. pub binary: Option, + /// Verifying spelling in file names. pub check_filename: Option, + /// Verifying spelling in files. pub check_file: Option, #[serde(flatten)] pub tokenizer: Option, @@ -295,17 +162,17 @@ impl EngineConfig { } } - pub fn update(&mut self, source: &dyn EngineSource) { - if let Some(source) = source.binary() { + pub fn update(&mut self, source: &EngineConfig) { + if let Some(source) = source.binary { self.binary = Some(source); } - if let Some(source) = source.check_filename() { + if let Some(source) = source.check_filename { self.check_filename = Some(source); } - if let Some(source) = source.check_file() { + if let Some(source) = source.check_file { self.check_file = Some(source); } - if let Some(source) = source.tokenizer() { + if let Some(source) = source.tokenizer.as_ref() { let mut tokenizer = None; std::mem::swap(&mut tokenizer, &mut self.tokenizer); let mut tokenizer = tokenizer.unwrap_or_default(); @@ -313,7 +180,7 @@ impl EngineConfig { let mut tokenizer = Some(tokenizer); std::mem::swap(&mut tokenizer, &mut self.tokenizer); } - if let Some(source) = source.dict() { + if let Some(source) = source.dict.as_ref() { let mut dict = None; std::mem::swap(&mut dict, &mut self.dict); let mut dict = dict.unwrap_or_default(); @@ -336,36 +203,19 @@ impl EngineConfig { } } -impl EngineSource for EngineConfig { - fn binary(&self) -> Option { - self.binary - } - - fn check_filename(&self) -> Option { - self.check_filename - } - - fn check_file(&self) -> Option { - self.check_file - } - - fn tokenizer(&self) -> Option<&dyn TokenizerSource> { - self.tokenizer.as_ref().map(|t| t as &dyn TokenizerSource) - } - - fn dict(&self) -> Option<&dyn DictSource> { - self.dict.as_ref().map(|d| d as &dyn DictSource) - } -} - #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields, default)] #[serde(rename_all = "kebab-case")] pub struct TokenizerConfig { + /// Do not check identifiers that appear to be hexadecimal values. pub ignore_hex: Option, + /// Allow identifiers to start with digits, in addition to letters. pub identifier_leading_digits: Option, + /// Allow identifiers to start with one of these characters. pub identifier_leading_chars: Option, + /// Allow identifiers to include digits, in addition to letters. pub identifier_include_digits: Option, + /// Allow identifiers to include these characters. pub identifier_include_chars: Option, } @@ -385,21 +235,21 @@ impl TokenizerConfig { } } - pub fn update(&mut self, source: &dyn TokenizerSource) { - if let Some(source) = source.ignore_hex() { + pub fn update(&mut self, source: &TokenizerConfig) { + if let Some(source) = source.ignore_hex { self.ignore_hex = Some(source); } - if let Some(source) = source.identifier_leading_digits() { + if let Some(source) = source.identifier_leading_digits { self.identifier_leading_digits = Some(source); } - if let Some(source) = source.identifier_leading_chars() { - self.identifier_leading_chars = Some(kstring::KString::from_ref(source)); + if let Some(source) = source.identifier_leading_chars.as_ref() { + self.identifier_leading_chars = Some(source.clone()); } - if let Some(source) = source.identifier_include_digits() { + 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(kstring::KString::from_ref(source)); + if let Some(source) = source.identifier_include_chars.as_ref() { + self.identifier_include_chars = Some(source.clone()); } } @@ -424,28 +274,6 @@ impl TokenizerConfig { } } -impl TokenizerSource for TokenizerConfig { - fn ignore_hex(&self) -> Option { - self.ignore_hex - } - - fn identifier_leading_digits(&self) -> Option { - self.identifier_leading_digits - } - - fn identifier_leading_chars(&self) -> Option<&str> { - self.identifier_leading_chars.as_deref() - } - - fn identifier_include_digits(&self) -> Option { - self.identifier_include_digits - } - - fn identifier_include_chars(&self) -> Option<&str> { - self.identifier_include_chars.as_deref() - } -} - #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields, default)] #[serde(rename_all = "kebab-case")] @@ -465,19 +293,21 @@ impl DictConfig { } } - pub fn update(&mut self, source: &dyn DictSource) { - if let Some(source) = source.locale() { + pub fn update(&mut self, source: &DictConfig) { + if let Some(source) = source.locale { self.locale = Some(source); } self.extend_identifiers.extend( source - .extend_identifiers() - .map(|(k, v)| (kstring::KString::from_ref(k), kstring::KString::from_ref(v))), + .extend_identifiers + .iter() + .map(|(key, value)| (key.clone(), value.clone())), ); self.extend_words.extend( source - .extend_words() - .map(|(k, v)| (kstring::KString::from_ref(k), kstring::KString::from_ref(v))), + .extend_words + .iter() + .map(|(key, value)| (key.clone(), value.clone())), ); } @@ -502,28 +332,6 @@ impl DictConfig { } } -impl DictSource for DictConfig { - fn locale(&self) -> Option { - self.locale - } - - fn extend_identifiers(&self) -> Box + '_> { - Box::new( - self.extend_identifiers - .iter() - .map(|(k, v)| (k.as_str(), v.as_str())), - ) - } - - fn extend_words(&self) -> Box + '_> { - Box::new( - self.extend_words - .iter() - .map(|(k, v)| (k.as_str(), v.as_str())), - ) - } -} - fn find_project_file(dir: &std::path::Path, names: &[&str]) -> Option { let mut file_path = dir.join("placeholder"); for name in names { diff --git a/src/main.rs b/src/main.rs index 5cddf7c..0d9139d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,6 +35,8 @@ fn run() -> proc_exit::ExitResult { if let Some(output_path) = args.dump_config.as_ref() { run_dump_config(&args, output_path) + } else if args.type_list { + run_type_list(&args) } else { run_checks(&args) } @@ -58,14 +60,17 @@ fn run_dump_config(args: &args::Args, output_path: &std::path::Path) -> proc_exi }; let storage = typos_cli::policy::ConfigStorage::new(); - let mut overrides = config::EngineConfig::default(); - overrides.update(&args.overrides); let mut engine = typos_cli::policy::ConfigEngine::new(&storage); - engine.set_isolated(args.isolated).set_overrides(overrides); + engine.set_isolated(args.isolated); + + let mut overrides = config::Config::default(); if let Some(path) = args.custom_config.as_ref() { let custom = config::Config::from_file(path).with_code(proc_exit::Code::CONFIG_ERR)?; - engine.set_custom_config(custom); + overrides.update(&custom); } + overrides.update(&args.config.to_config()); + engine.set_overrides(overrides); + let config = engine .load_config(cwd) .with_code(proc_exit::Code::CONFIG_ERR)?; @@ -82,18 +87,68 @@ fn run_dump_config(args: &args::Args, output_path: &std::path::Path) -> proc_exi Ok(()) } +fn run_type_list(args: &args::Args) -> proc_exit::ExitResult { + let global_cwd = std::env::current_dir()?; + + let path = &args.path[0]; + let path = if path == std::path::Path::new("-") { + path.to_owned() + } else { + path.canonicalize().with_code(proc_exit::Code::USAGE_ERR)? + }; + let cwd = if path == std::path::Path::new("-") { + global_cwd.as_path() + } else if path.is_file() { + path.parent().unwrap() + } else { + path.as_path() + }; + + let storage = typos_cli::policy::ConfigStorage::new(); + let mut engine = typos_cli::policy::ConfigEngine::new(&storage); + engine.set_isolated(args.isolated); + + let mut overrides = config::Config::default(); + if let Some(path) = args.custom_config.as_ref() { + let custom = config::Config::from_file(path).with_code(proc_exit::Code::CONFIG_ERR)?; + overrides.update(&custom); + } + overrides.update(&args.config.to_config()); + engine.set_overrides(overrides); + + engine + .init_dir(cwd) + .with_code(proc_exit::Code::CONFIG_ERR)?; + let definitions = engine.file_types(cwd); + + let stdout = std::io::stdout(); + let mut handle = stdout.lock(); + for def in definitions { + writeln!( + handle, + "{}: {}", + def.name(), + itertools::join(def.globs(), ", ") + )?; + } + + Ok(()) +} + fn run_checks(args: &args::Args) -> proc_exit::ExitResult { let global_cwd = std::env::current_dir()?; let storage = typos_cli::policy::ConfigStorage::new(); - let mut overrides = config::EngineConfig::default(); - overrides.update(&args.overrides); let mut engine = typos_cli::policy::ConfigEngine::new(&storage); - engine.set_isolated(args.isolated).set_overrides(overrides); + engine.set_isolated(args.isolated); + + let mut overrides = config::Config::default(); if let Some(path) = args.custom_config.as_ref() { let custom = config::Config::from_file(path).with_code(proc_exit::Code::CONFIG_ERR)?; - engine.set_custom_config(custom); + overrides.update(&custom); } + overrides.update(&args.config.to_config()); + engine.set_overrides(overrides); let mut typos_found = false; let mut errors_found = false; @@ -114,19 +169,19 @@ fn run_checks(args: &args::Args) -> proc_exit::ExitResult { engine .init_dir(cwd) .with_code(proc_exit::Code::CONFIG_ERR)?; - let files = engine.files(cwd); + let walk_policy = engine.walk(cwd); let threads = if path.is_file() { 1 } else { args.threads }; let single_threaded = threads == 1; let mut walk = ignore::WalkBuilder::new(path); walk.threads(args.threads) - .hidden(files.ignore_hidden()) - .ignore(files.ignore_dot()) - .git_global(files.ignore_global()) - .git_ignore(files.ignore_vcs()) - .git_exclude(files.ignore_vcs()) - .parents(files.ignore_parent()); + .hidden(walk_policy.ignore_hidden()) + .ignore(walk_policy.ignore_dot()) + .git_global(walk_policy.ignore_global()) + .git_ignore(walk_policy.ignore_vcs()) + .git_exclude(walk_policy.ignore_vcs()) + .parents(walk_policy.ignore_parent()); // HACK: Diff doesn't handle mixing content let output_reporter = if args.diff { diff --git a/src/policy.rs b/src/policy.rs index 92c8aa6..cded1c9 100644 --- a/src/policy.rs +++ b/src/policy.rs @@ -35,12 +35,11 @@ impl Default for ConfigStorage { pub struct ConfigEngine<'s> { storage: &'s ConfigStorage, - overrides: Option, - custom: Option, + overrides: Option, isolated: bool, configs: std::collections::HashMap, - files: Intern, + walk: Intern, tokenizer: Intern, dict: Intern>, } @@ -50,61 +49,62 @@ impl<'s> ConfigEngine<'s> { Self { storage, overrides: Default::default(), - custom: Default::default(), configs: Default::default(), isolated: false, - files: Default::default(), + walk: Default::default(), tokenizer: Default::default(), dict: Default::default(), } } - pub fn set_overrides(&mut self, overrides: crate::config::EngineConfig) -> &mut Self { + pub fn set_overrides(&mut self, overrides: crate::config::Config) -> &mut Self { self.overrides = Some(overrides); self } - pub fn set_custom_config(&mut self, custom: crate::config::Config) -> &mut Self { - self.custom = Some(custom); - self - } - pub fn set_isolated(&mut self, isolated: bool) -> &mut Self { self.isolated = isolated; self } - pub fn files(&mut self, cwd: &std::path::Path) -> &crate::config::Walk { + pub fn walk(&self, cwd: &std::path::Path) -> &crate::config::Walk { let dir = self .configs .get(cwd) .expect("`init_dir` must be called first"); - self.get_files(dir) + self.get_walk(dir) + } + + pub fn file_types(&self, cwd: &std::path::Path) -> &[ignore::types::FileTypeDef] { + let dir = self + .configs + .get(cwd) + .expect("`init_dir` must be called first"); + dir.type_matcher.definitions() } pub fn policy(&self, path: &std::path::Path) -> Policy<'_, '_> { - let dir = self - .get_dir(path) - .expect("`files()` should be called first"); + let dir = self.get_dir(path).expect("`walk()` should be called first"); + let file_config = dir.get_file_config(path); Policy { - check_filenames: dir.check_filenames, - check_files: dir.check_files, - binary: dir.binary, - tokenizer: self.get_tokenizer(dir), - dict: self.get_dict(dir), + check_filenames: file_config.check_filenames, + check_files: file_config.check_files, + binary: file_config.binary, + tokenizer: self.get_tokenizer(&file_config), + dict: self.get_dict(&file_config), } } - fn get_files(&self, dir: &DirConfig) -> &crate::config::Walk { - self.files.get(dir.files) + fn get_walk(&self, dir: &DirConfig) -> &crate::config::Walk { + self.walk.get(dir.walk) } - fn get_tokenizer(&self, dir: &DirConfig) -> &typos::tokens::Tokenizer { - self.tokenizer.get(dir.tokenizer) + fn get_tokenizer(&self, file: &FileConfig) -> &typos::tokens::Tokenizer { + self.tokenizer.get(file.tokenizer) } - fn get_dict(&self, dir: &DirConfig) -> &dyn typos::Dictionary { - self.dict.get(dir.dict) + fn get_dict(&self, file: &FileConfig) -> &dyn typos::Dictionary { + self.dict.get(file.dict) } fn get_dir(&self, path: &std::path::Path) -> Option<&DirConfig> { @@ -127,11 +127,8 @@ impl<'s> ConfigEngine<'s> { config.update(&derived); } } - if let Some(custom) = self.custom.as_ref() { - config.update(custom); - } if let Some(overrides) = self.overrides.as_ref() { - config.default.update(overrides); + config.update(overrides); } Ok(config) @@ -143,14 +140,46 @@ impl<'s> ConfigEngine<'s> { } let config = self.load_config(cwd)?; + let crate::config::Config { + files, + mut default, + type_, + overrides, + } = config; - let crate::config::Config { files, default } = config; - let binary = default.binary(); - let check_filename = default.check_filename(); - let check_file = default.check_file(); + let walk = self.walk.intern(files); + + let types = type_ + .into_iter() + .map(|(type_, type_engine)| { + let mut new_type_engine = default.clone(); + new_type_engine.update(&type_engine); + new_type_engine.update(&overrides); + let type_config = self.init_file_config(new_type_engine); + (type_, type_config) + }) + .collect(); + default.update(&overrides); + let default = self.init_file_config(default); + + let dir = DirConfig { + walk, + default, + types, + type_matcher: ignore::types::TypesBuilder::new().add_defaults().build()?, + }; + + self.configs.insert(cwd.to_owned(), dir); + Ok(()) + } + + fn init_file_config(&mut self, engine: crate::config::EngineConfig) -> FileConfig { + let binary = engine.binary(); + let check_filename = engine.check_filename(); + let check_file = engine.check_file(); let crate::config::EngineConfig { tokenizer, dict, .. - } = default; + } = engine; let tokenizer_config = tokenizer.unwrap_or_else(crate::config::TokenizerConfig::from_defaults); let dict_config = dict.unwrap_or_else(crate::config::DictConfig::from_defaults); @@ -177,20 +206,15 @@ impl<'s> ConfigEngine<'s> { ); let dict = self.dict.intern(dict); - let files = self.files.intern(files); let tokenizer = self.tokenizer.intern(tokenizer); - let dir = DirConfig { - files, + FileConfig { check_filenames: check_filename, check_files: check_file, binary, tokenizer, dict, - }; - - self.configs.insert(cwd.to_owned(), dir); - Ok(()) + } } } @@ -222,8 +246,29 @@ impl Default for Intern { } } +#[derive(Clone, Debug)] struct DirConfig { - files: usize, + walk: usize, + default: FileConfig, + types: std::collections::HashMap, + type_matcher: ignore::types::Types, +} + +impl DirConfig { + fn get_file_config(&self, path: &std::path::Path) -> FileConfig { + let match_ = self.type_matcher.matched(path, false); + let name = match_ + .inner() + .and_then(|g| g.file_type_def()) + .map(|f| f.name()); + + name.and_then(|name| self.types.get(name).copied()) + .unwrap_or(self.default) + } +} + +#[derive(Copy, Clone, Debug)] +struct FileConfig { tokenizer: usize, dict: usize, check_filenames: bool,