use crate::expr::Expr;
use crate::expr::FirstMatchOf;
use crate::expr::SequenceExpr;
use crate::expr::WordExprGroup;
use hashbrown::HashMap;
use super::{ExprLinter, Lint, LintKind, Suggestion};
use crate::linting::expr_linter::Chunk;
use crate::{Token, TokenStringExt};
pub struct DotInitialisms {
expr: WordExprGroup<FirstMatchOf>,
corrections: HashMap<&'static str, &'static str>,
}
impl Default for DotInitialisms {
fn default() -> Self {
let mut patterns = WordExprGroup::default();
let mut corrections = HashMap::new();
corrections.insert("ie", "i.e.");
corrections.insert("eg", "e.g.");
for target in corrections.keys() {
let pattern = SequenceExpr::default()
.then_exact_word(target)
.then_punctuation();
patterns.add(target, pattern);
}
Self {
expr: patterns,
corrections,
}
}
}
impl ExprLinter for DotInitialisms {
type Unit = Chunk;
fn expr(&self) -> &dyn Expr {
&self.expr
}
fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Option<Lint> {
let found_word_tok = matched_tokens.first()?;
let found_word = found_word_tok.get_str(source);
let correction = self.corrections.get(found_word.as_str())?;
Some(Lint {
span: matched_tokens.span()?,
lint_kind: LintKind::Formatting,
suggestions: vec![Suggestion::ReplaceWith(correction.chars().collect())],
message: "Initialisms should have dot-separated letters.".to_owned(),
priority: 63,
})
}
fn description(&self) -> &'static str {
"Ensures common initialisms (like \"i.e.\") are properly dot-separated."
}
}
#[cfg(test)]
mod tests {
use super::DotInitialisms;
use crate::linting::tests::assert_suggestion_result;
#[test]
fn matches_eg() {
assert_suggestion_result(
"Some text here (eg. more text).",
DotInitialisms::default(),
"Some text here (e.g. more text).",
)
}
}