Skip to content

Commit f840599

Browse files
authored
Merge pull request #854 from epage/word
feat(config): Allow ignoring words by regex
2 parents 35a8bc6 + be8628f commit f840599

File tree

8 files changed

+58
-6
lines changed

8 files changed

+58
-6
lines changed

crates/typos-cli/src/config.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,8 @@ pub struct DictConfig {
425425
#[serde(with = "serde_regex")]
426426
pub extend_ignore_identifiers_re: Vec<regex::Regex>,
427427
pub extend_identifiers: HashMap<kstring::KString, kstring::KString>,
428+
#[serde(with = "serde_regex")]
429+
pub extend_ignore_words_re: Vec<regex::Regex>,
428430
pub extend_words: HashMap<kstring::KString, kstring::KString>,
429431
}
430432

@@ -435,6 +437,7 @@ impl DictConfig {
435437
locale: Some(empty.locale()),
436438
extend_ignore_identifiers_re: Default::default(),
437439
extend_identifiers: Default::default(),
440+
extend_ignore_words_re: Default::default(),
438441
extend_words: Default::default(),
439442
}
440443
}
@@ -451,6 +454,8 @@ impl DictConfig {
451454
.iter()
452455
.map(|(key, value)| (key.clone(), value.clone())),
453456
);
457+
self.extend_ignore_words_re
458+
.extend(source.extend_ignore_words_re.iter().cloned());
454459
self.extend_words.extend(
455460
source
456461
.extend_words
@@ -475,6 +480,10 @@ impl DictConfig {
475480
)
476481
}
477482

483+
pub fn extend_ignore_words_re(&self) -> Box<dyn Iterator<Item = &regex::Regex> + '_> {
484+
Box::new(self.extend_ignore_words_re.iter())
485+
}
486+
478487
pub fn extend_words(&self) -> Box<dyn Iterator<Item = (&str, &str)> + '_> {
479488
Box::new(
480489
self.extend_words
@@ -503,6 +512,11 @@ impl PartialEq for DictConfig {
503512
.map(|r| r.as_str())
504513
.eq(rhs.extend_ignore_identifiers_re.iter().map(|r| r.as_str()))
505514
&& self.extend_identifiers == rhs.extend_identifiers
515+
&& self
516+
.extend_ignore_words_re
517+
.iter()
518+
.map(|r| r.as_str())
519+
.eq(rhs.extend_ignore_words_re.iter().map(|r| r.as_str()))
506520
&& self.extend_words == rhs.extend_words
507521
}
508522
}

crates/typos-cli/src/dict.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ fn case_correct(correction: &mut Cow<'_, str>, case: Case) {
216216
pub struct Override<'i, 'w, D> {
217217
ignored_identifiers: Vec<regex::Regex>,
218218
identifiers: HashMap<&'i str, Status<'i>, ahash::RandomState>,
219+
ignored_words: Vec<regex::Regex>,
219220
words: HashMap<unicase::UniCase<&'w str>, Status<'w>, ahash::RandomState>,
220221
inner: D,
221222
}
@@ -225,6 +226,7 @@ impl<'i, 'w, D: typos::Dictionary> Override<'i, 'w, D> {
225226
Self {
226227
ignored_identifiers: Default::default(),
227228
identifiers: Default::default(),
229+
ignored_words: Default::default(),
228230
words: Default::default(),
229231
inner,
230232
}
@@ -238,6 +240,10 @@ impl<'i, 'w, D: typos::Dictionary> Override<'i, 'w, D> {
238240
self.identifiers = Self::interpret(identifiers).collect();
239241
}
240242

243+
pub fn ignored_words<'r>(&mut self, ignored: impl Iterator<Item = &'r regex::Regex>) {
244+
self.ignored_words.extend(ignored.cloned());
245+
}
246+
241247
pub fn words<I: Iterator<Item = (&'w str, &'w str)>>(&mut self, words: I) {
242248
self.words = Self::interpret(words)
243249
.map(|(k, v)| (UniCase::new(k), v))
@@ -283,15 +289,22 @@ impl<'i, 'w, D: typos::Dictionary> typos::Dictionary for Override<'i, 'w, D> {
283289
return None;
284290
}
285291

292+
for ignored in &self.ignored_words {
293+
if ignored.is_match(word.token()) {
294+
return Some(Status::Valid);
295+
}
296+
}
297+
286298
// Skip hashing if we can
287-
let custom = if !self.words.is_empty() {
299+
if !self.words.is_empty() {
288300
let w = UniCase::new(word.token());
289301
// HACK: couldn't figure out the lifetime issue with replacing `cloned` with `borrow`
290-
self.words.get(&w).cloned()
291-
} else {
292-
None
293-
};
294-
custom.or_else(|| self.inner.correct_word(word))
302+
if let Some(status) = self.words.get(&w).cloned() {
303+
return Some(status);
304+
}
305+
}
306+
307+
self.inner.correct_word(word)
295308
}
296309
}
297310

crates/typos-cli/src/policy.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ impl<'s> ConfigEngine<'s> {
255255
.extend_identifiers()
256256
.map(|(k, v)| (self.storage.get(k), self.storage.get(v))),
257257
);
258+
dict.ignored_words(dict_config.extend_ignore_words_re());
258259
dict.words(
259260
dict_config
260261
.extend_words()
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[default.extend-words]
2+
hello = "goodbye"
3+
4+
[type.fail]
5+
extend-glob = ["*.fail"]
6+
7+
[type.ignore]
8+
extend-glob = ["*.ignore"]
9+
extend-ignore-words-re = ["he.*"]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
hello
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
hello
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
bin.name = "typos"
2+
status.code = 2
3+
stdin = ""
4+
stdout = """
5+
error: `hello` should be `goodbye`
6+
--> ./file.fail:1:1
7+
|
8+
1 | hello
9+
| ^^^^^
10+
|
11+
"""
12+
stderr = ""

docs/reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Configuration is read from the following (in precedence order)
3434
| default.extend-identifiers | \- | table of strings | Corrections for [identifiers](./design.md#identifiers-and-words). When the correction is blank, the identifier is never valid. When the correction is the key, the identifier is always valid. |
3535
| default.extend-ignore-identifiers-re | \- | list of [regexes](https://docs.rs/regex/latest/regex/index.html#syntax) | Pattern-match always-valid identifiers |
3636
| default.extend-words | \- | table of strings | Corrections for [words](./design.md#identifiers-and-words). When the correction is blank, the word is never valid. When the correction is the key, the word is always valid. |
37+
| default.extend-ignore-words-re | \- | list of [regexes](https://docs.rs/regex/latest/regex/index.html#syntax) | Pattern-match always-valid words. Note: you must handle case insensitivity yourself |
3738
| type.\<name>.\<field> | \<varied> | \<varied> | See `default.` for child keys. Run with `--type-list` to see available `<name>`s |
3839
| type.\<name>.extend-glob | \- | list of strings | File globs for matching `<name>` |
3940

0 commit comments

Comments
 (0)