From cd7e30b2c4e890976695494d97f93de43be78140 Mon Sep 17 00:00:00 2001 From: hippietrail Date: Fri, 22 Aug 2025 23:00:48 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20(wip)=20in=20(a)=20good=20condition(s)?= =?UTF-8?q?=20=E2=86=92=20in=20good=20condition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/linting/in_adjective_condition.rs | 112 ++++++++++++++++++ harper-core/src/linting/lint_group.rs | 2 + harper-core/src/linting/mod.rs | 2 + 3 files changed, 116 insertions(+) create mode 100644 harper-core/src/linting/in_adjective_condition.rs diff --git a/harper-core/src/linting/in_adjective_condition.rs b/harper-core/src/linting/in_adjective_condition.rs new file mode 100644 index 000000000..40f77e250 --- /dev/null +++ b/harper-core/src/linting/in_adjective_condition.rs @@ -0,0 +1,112 @@ +//! Linter for correcting common errors with the phrase "in [adjective] condition". + +use crate::{ + Token, + expr::{Expr, SequenceExpr}, + linting::{ExprLinter, Lint, LintKind, Suggestion}, + token_string_ext::TokenStringExt, +}; + +/// Linter that corrects common errors with phrases like "in good condition". +/// +/// Handles two cases: +/// 1. "in [a/an] [adjective] condition" -> "in [adjective] condition" +/// 2. "in [adjective] conditions" -> "in [adjective] condition" +pub struct InAdjectiveCondition { + expr: Box, +} + +impl Default for InAdjectiveCondition { + fn default() -> Self { + let singular = SequenceExpr::default() + .then_indefinite_article() + .t_ws() + .then_adjective() + .t_ws() + .t_aco("condition"); + + let plural = SequenceExpr::default() + .then_adjective() + .t_ws() + .t_aco("conditions"); + + Self { + expr: Box::new( + SequenceExpr::default() + .t_aco("in") + .t_ws() + .then_any_of(vec![Box::new(singular), Box::new(plural)]), + ), + } + } +} + +impl ExprLinter for InAdjectiveCondition { + fn expr(&self) -> &dyn Expr { + self.expr.as_ref() + } + + fn match_to_lint(&self, toks: &[Token], src: &[char]) -> Option { + let (span, sugg, msg) = match toks.len() { + 5 => ( + toks.last()?.span, + Suggestion::replace_with_match_case( + "condition".chars().collect(), + toks.last()?.span.get_content(src), + ), + "`Condition` should be singular.", + ), + 7 => ( + toks[1..3].span()?, + Suggestion::Remove, + "An indefinite article should not be used here.", + ), + _ => return None, + }; + + Some(Lint { + span, + lint_kind: LintKind::Grammar, + suggestions: vec![sugg], + message: msg.to_string(), + ..Default::default() + }) + } + + fn description(&self) -> &str { + "Corrects incorrect variants of `in good condition` with an indefinite article or plural." + } +} + +#[cfg(test)] +mod tests { + use super::InAdjectiveCondition; + use crate::linting::tests::assert_suggestion_result; + + #[test] + fn fix_in_good_conditions() { + assert_suggestion_result( + "in good conditions", + InAdjectiveCondition::default(), + "in good condition", + ); + } + + #[test] + fn fix_in_a_bad_condition() { + assert_suggestion_result( + "in a bad condition", + InAdjectiveCondition::default(), + "in bad condition", + ); + } + + #[test] + fn fix_great_condition_all_caps() { + assert_suggestion_result( + "YEAH IT'S IN GREAT CONDITIONS REALLY!", + InAdjectiveCondition::default(), + "YEAH IT'S IN GREAT CONDITION REALLY!", + ) + } +} diff --git a/harper-core/src/linting/lint_group.rs b/harper-core/src/linting/lint_group.rs index 641585af5..44878e9bd 100644 --- a/harper-core/src/linting/lint_group.rs +++ b/harper-core/src/linting/lint_group.rs @@ -52,6 +52,7 @@ use super::hop_hope::HopHope; use super::how_to::HowTo; use super::hyphenate_number_day::HyphenateNumberDay; use super::i_am_agreement::IAmAgreement; +use super::in_adjective_condition::InAdjectiveCondition; use super::in_on_the_cards::InOnTheCards; use super::inflected_verb_after_to::InflectedVerbAfterTo; use super::its_contraction::ItsContraction; @@ -442,6 +443,7 @@ impl LintGroup { insert_struct_rule!(HowTo, true); insert_expr_rule!(HyphenateNumberDay, true); insert_expr_rule!(IAmAgreement, true); + insert_expr_rule!(InAdjectiveCondition, true); insert_struct_rule!(ItsContraction, true); insert_struct_rule!(ItsPossessive, true); insert_expr_rule!(LeftRightHand, true); diff --git a/harper-core/src/linting/mod.rs b/harper-core/src/linting/mod.rs index de41f8156..ea95c4f04 100644 --- a/harper-core/src/linting/mod.rs +++ b/harper-core/src/linting/mod.rs @@ -47,6 +47,7 @@ mod hope_youre; mod how_to; mod hyphenate_number_day; mod i_am_agreement; +mod in_adjective_condition; mod in_on_the_cards; mod inflected_verb_after_to; mod initialism_linter; @@ -176,6 +177,7 @@ pub use hop_hope::HopHope; pub use how_to::HowTo; pub use hyphenate_number_day::HyphenateNumberDay; pub use i_am_agreement::IAmAgreement; +pub use in_adjective_condition::InAdjectiveCondition; pub use in_on_the_cards::InOnTheCards; pub use inflected_verb_after_to::InflectedVerbAfterTo; pub use initialism_linter::InitialismLinter;