use crate::linting::expr_linter::Chunk;
use crate::{
Lrc, Token, TokenStringExt,
expr::{Expr, FirstMatchOf, FixedPhrase, SequenceExpr},
linting::{ExprLinter, Lint, LintKind, Suggestion},
patterns::WordSet,
};
pub struct RedundantAdditiveAdverbs {
expr: SequenceExpr,
}
impl Default for RedundantAdditiveAdverbs {
fn default() -> Self {
let also_too = WordSet::new(&["also", "too"]);
let as_well = FixedPhrase::from_phrase("as well");
let additive_adverb = Lrc::new(FirstMatchOf::new(vec![
Box::new(also_too),
Box::new(as_well),
]));
let multiple_additive_adverbs = SequenceExpr::with(additive_adverb.clone())
.then_one_or_more(SequenceExpr::whitespace().then(additive_adverb.clone()))
.then_optional(SequenceExpr::whitespace().t_aco("as"));
Self {
expr: multiple_additive_adverbs,
}
}
}
impl ExprLinter for RedundantAdditiveAdverbs {
type Unit = Chunk;
fn expr(&self) -> &dyn Expr {
&self.expr
}
fn match_to_lint(&self, toks: &[Token], src: &[char]) -> Option<Lint> {
let phrase_string = toks.span()?.get_content_string(src).to_lowercase();
if phrase_string.eq("also too") {
return None;
}
let mut toks = toks;
if phrase_string.ends_with(" as well as") {
if toks.len() >= 6 {
toks = &toks[..toks.len() - 6];
}
}
let mut additive_adverbs: Vec<&[char]> = vec![];
for word in toks
.iter()
.filter(|tok| tok.kind.is_word())
.map(|tok| tok.get_ch(src))
.collect::<Vec<_>>()
{
let term: &[char] = match word {
['a', 's'] | ['w', 'e', 'l', 'l'] => &['a', 's', ' ', 'w', 'e', 'l', 'l'],
_ => word,
};
if !additive_adverbs.contains(&term) {
additive_adverbs.push(term);
}
}
if additive_adverbs.len() < 2 {
return None;
}
let suggestions = additive_adverbs
.iter()
.filter_map(|adverb| {
Some(Suggestion::replace_with_match_case(
adverb.to_vec(),
toks.span()?.get_content(src),
))
})
.collect::<Vec<_>>();
let message = format!(
"Use just one of `{}`.",
additive_adverbs
.iter()
.map(|s| s.iter().collect::<String>())
.collect::<Vec<_>>()
.join("` or `")
);
Some(Lint {
span: toks.span()?,
lint_kind: LintKind::Redundancy,
suggestions,
message,
priority: 31,
})
}
fn description(&self) -> &'static str {
"Detects redundant additive adverbs."
}
}
#[cfg(test)]
mod tests {
use super::RedundantAdditiveAdverbs;
use crate::linting::tests::{assert_lint_count, assert_suggestion_result};
#[test]
fn flag_as_well_too() {
assert_suggestion_result(
"Yeah, we definitely miss him on this episode here, but you could probably get him on a podcast that's more focused on what Equinix is doing as well too, specifically.",
RedundantAdditiveAdverbs::default(),
"Yeah, we definitely miss him on this episode here, but you could probably get him on a podcast that's more focused on what Equinix is doing as well, specifically.",
);
}
#[test]
fn flag_too_also() {
assert_suggestion_result(
"The #1 uptime service with many servers and is easy to setup. It is free too also.",
RedundantAdditiveAdverbs::default(),
"The #1 uptime service with many servers and is easy to setup. It is free also.",
);
}
#[test]
fn dont_flag_also_too() {
assert_lint_count(
"The version update is also too slow.",
RedundantAdditiveAdverbs::default(),
0,
);
}
#[test]
fn dont_flag_also_as_well_as() {
assert_lint_count(
"Believe there are stable packages in the readme also as well as a link to an old version of forge in the ...",
RedundantAdditiveAdverbs::default(),
0,
);
}
#[test]
fn do_flag_too_also_as_well_as() {
assert_lint_count(
"What would happen with a sentence that included too also as well as?",
RedundantAdditiveAdverbs::default(),
1,
);
}
#[test]
fn flag_too_as_well() {
assert_suggestion_result(
"Module name itself was changed too as well.",
RedundantAdditiveAdverbs::default(),
"Module name itself was changed as well.",
);
}
}