use crate::expr::Expr;
use crate::expr::FirstMatchOf;
use crate::expr::SequenceExpr;
use crate::linting::expr_linter::Chunk;
use crate::{
Token, TokenStringExt,
linting::{ExprLinter, Lint, LintKind, Suggestion},
};
pub struct TheHowWhy {
expr: FirstMatchOf,
}
impl Default for TheHowWhy {
fn default() -> Self {
let the_how = SequenceExpr::default()
.t_aco("the")
.then_whitespace()
.t_aco("how")
.then_unless(SequenceExpr::whitespace().t_aco("to"));
let the_who = SequenceExpr::default()
.t_aco("the")
.then_whitespace()
.t_aco("who")
.then_unless(
SequenceExpr::whitespace()
.t_aco("'s")
.then_whitespace()
.t_aco("who"),
);
let the_why = SequenceExpr::default()
.t_aco("the")
.then_whitespace()
.t_aco("why");
let the_when = SequenceExpr::default()
.t_aco("the")
.then_whitespace()
.t_aco("when");
let the_what = SequenceExpr::default()
.t_aco("the")
.then_whitespace()
.t_aco("what");
let expr = FirstMatchOf::new(vec![
Box::new(the_how),
Box::new(the_who),
Box::new(the_why),
Box::new(the_when),
Box::new(the_what),
]);
Self { expr }
}
}
impl ExprLinter for TheHowWhy {
type Unit = Chunk;
fn expr(&self) -> &dyn Expr {
&self.expr
}
fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Option<Lint> {
let the_token_span = matched_tokens[0..2].span()?;
let question_word_token = matched_tokens.get(2)?;
let question_word = question_word_token.get_ch(source);
Some(Lint {
span: the_token_span,
lint_kind: LintKind::Miscellaneous,
message: format!(
"Remove `the` before `{}`. In most contexts, `{}` alone is clearer.",
question_word.iter().collect::<String>(),
question_word.iter().collect::<String>()
),
suggestions: vec![Suggestion::Remove],
priority: 31,
})
}
fn description(&self) -> &str {
"Removes the extra `the` from expressions like `the how`, skipping `how to` and `who's who`."
}
}
#[cfg(test)]
mod tests {
use super::TheHowWhy;
use crate::linting::tests::{assert_lint_count, assert_suggestion_result};
#[test]
fn basic_the_how() {
assert_suggestion_result(
"This is the how it all started.",
TheHowWhy::default(),
"This is how it all started.",
);
}
#[test]
fn the_why() {
assert_suggestion_result(
"The important part is the why it matters.",
TheHowWhy::default(),
"The important part is why it matters.",
);
}
#[test]
fn skip_how_to() {
assert_lint_count(
"I'd like to explain the how to install this properly.",
TheHowWhy::default(),
0,
);
}
#[test]
fn skip_whos_who() {
assert_lint_count(
"We covered the who's who of corporate leadership last time.",
TheHowWhy::default(),
0,
);
}
#[test]
fn the_who() {
assert_suggestion_result(
"We must identify the who is responsible.",
TheHowWhy::default(),
"We must identify who is responsible.",
);
}
#[test]
fn the_when() {
assert_suggestion_result(
"He outlined the when the new phase will start.",
TheHowWhy::default(),
"He outlined when the new phase will start.",
);
}
#[test]
fn the_what() {
assert_suggestion_result(
"The presentation clarifies the what we intend to build.",
TheHowWhy::default(),
"The presentation clarifies what we intend to build.",
);
}
#[test]
fn no_false_positive() {
assert_lint_count(
"These tips examine the how to fix your code quickly, plus the what's next.",
TheHowWhy::default(),
0,
);
}
}