harper_core/linting/
initialism_linter.rs

1use crate::expr::Expr;
2use itertools::Itertools;
3
4use crate::{Token, patterns::Word};
5
6use super::{ExprLinter, Lint, LintKind, Suggestion};
7use crate::linting::expr_linter::Chunk;
8
9/// A struct that can be composed to expand initialisms, respecting the capitalization of each
10/// item.
11pub struct InitialismLinter {
12    expr: Box<dyn Expr>,
13    /// The lowercase-normalized expansion of the initialism.
14    expansion_lower: Vec<Vec<char>>,
15}
16
17impl InitialismLinter {
18    /// Construct a linter that can correct an initialism to
19    pub fn new(initialism: &str, expansion: &str) -> Self {
20        let expansion_lower = expansion
21            .split(' ')
22            .map(|s| s.chars().map(|v| v.to_ascii_lowercase()).collect())
23            .collect();
24
25        Self {
26            expr: Box::new(Word::from_char_string(initialism.chars().collect())),
27            expansion_lower,
28        }
29    }
30}
31
32impl ExprLinter for InitialismLinter {
33    type Unit = Chunk;
34
35    fn expr(&self) -> &dyn Expr {
36        self.expr.as_ref()
37    }
38
39    fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Option<Lint> {
40        let tok = matched_tokens.first()?;
41        let source = tok.span.get_content(source);
42
43        let mut expansion_lower = self.expansion_lower.to_owned();
44        let first_letter = &mut expansion_lower[0][0];
45
46        *first_letter = if source[0].is_ascii_uppercase() {
47            first_letter.to_ascii_uppercase()
48        } else {
49            first_letter.to_ascii_lowercase()
50        };
51
52        let phrase = Itertools::intersperse_with(expansion_lower.into_iter(), || vec![' '])
53            .reduce(|mut left, mut right| {
54                left.append(&mut right);
55                left
56            })
57            .unwrap();
58
59        Some(Lint {
60            span: tok.span,
61            lint_kind: LintKind::Miscellaneous,
62            suggestions: vec![Suggestion::ReplaceWith(phrase)],
63            message: "Try expanding this initialism.".to_owned(),
64            priority: 127,
65        })
66    }
67
68    fn description(&self) -> &'static str {
69        "Expands an initialism."
70    }
71}
72
73#[cfg(test)]
74mod tests {}