use crate::{
CharStringExt, Lint, Token, TokenStringExt,
expr::{All, Expr, OwnedExprExt, SequenceExpr},
linting::{ExprLinter, LintKind, Suggestion, expr_linter::Chunk},
patterns::WordSet,
};
enum Fork<K, S, T> {
Kind(K),
Sort(S),
Type(T),
}
impl<K, S, T, Item> Iterator for Fork<K, S, T>
where
K: Iterator<Item = Item>,
S: Iterator<Item = Item>,
T: Iterator<Item = Item>,
{
type Item = Item;
fn next(&mut self) -> Option<Self::Item> {
match self {
Fork::Kind(k) => k.next(),
Fork::Sort(s) => s.next(),
Fork::Type(t) => t.next(),
}
}
}
const DEGREE_ADVERB_BLACKLIST: [&str; 5] = ["comparably", "indeed", "notably", "really", "so"];
pub struct VeryLess {
expr: All,
}
impl Default for VeryLess {
fn default() -> Self {
Self {
expr: SequenceExpr::default()
.then_degree_adverb()
.t_ws()
.t_aco("less")
.but_not(WordSet::new(DEGREE_ADVERB_BLACKLIST)),
}
}
}
impl ExprLinter for VeryLess {
type Unit = Chunk;
fn match_to_lint(&self, toks: &[Token], src: &[char]) -> Option<Lint> {
let span = toks.span()?;
let [deg_tok, ws_tok, less_tok] = toks else {
return None;
};
let replace_less = ["few", "little"];
let replace_very = ["a lot", "far", "much"];
let replace_quite = ["quite a bit", "quite a lot"];
let corrections = if deg_tok.get_ch(src).eq_str("very") {
Fork::Kind(replace_very.iter().map(|v| {
v.chars()
.chain(ws_tok.get_ch(src).iter().copied())
.chain(less_tok.get_ch(src).iter().copied())
.collect::<Vec<char>>()
}))
} else if deg_tok.get_ch(src).eq_str("quite") {
Fork::Sort(replace_quite.iter().map(|q| {
q.chars()
.chain(ws_tok.get_ch(src).iter().copied())
.chain(less_tok.get_ch(src).iter().copied())
.collect::<Vec<char>>()
}))
} else {
Fork::Type(replace_less.iter().map(|l| {
deg_tok
.get_ch(src)
.iter()
.copied()
.chain(ws_tok.get_ch(src).iter().copied())
.chain(l.chars())
.collect::<Vec<char>>()
}))
};
let suggestions = corrections
.map(|c| Suggestion::replace_with_match_case(c, span.get_content(src)))
.collect();
Some(Lint {
span,
lint_kind: LintKind::Usage,
suggestions,
message: "English does not use words like ‘too’ or ‘very’ to modify ‘less’. \
• For comparisons, use ‘far less’ or ‘much less’, etc. \
• If describing a small amoung or quantity, use ‘very little’ (furniture, information, traffic, etc.) or ‘very few’ (items, people, things, etc.)."
.to_owned(),
..Default::default()
})
}
fn expr(&self) -> &dyn Expr {
&self.expr
}
fn description(&self) -> &str {
"Corrects adverbs of degree (`too`, `very`, etc.) used with `less` mostly in the writing of native German speakers.`"
}
}
#[cfg(test)]
mod tests {
use crate::linting::tests::{assert_no_lints, assert_suggestion_result};
use super::VeryLess;
#[test]
fn very_to_much() {
assert_suggestion_result(
"here is a simple way to do it with very less coding ... ;)",
VeryLess::default(),
"here is a simple way to do it with much less coding ... ;)",
);
}
#[test]
fn very_to_far() {
assert_suggestion_result(
"algorithm for processing large datasets with very less pre-configuration",
VeryLess::default(),
"algorithm for processing large datasets with far less pre-configuration",
);
}
#[test]
fn very_to_a_lot() {
assert_suggestion_result(
"Also the gpu memory usage is very less.",
VeryLess::default(),
"Also the gpu memory usage is a lot less.",
);
}
#[test]
fn too_less() {
assert_suggestion_result(
"Too much code for too less, maybe too many requests for only 10$.. who knows?!",
VeryLess::default(),
"Too much code for too little, maybe too many requests for only 10$.. who knows?!",
);
}
#[test]
fn too_less_info() {
assert_suggestion_result(
"codex showing too less information when he is think chain.",
VeryLess::default(),
"codex showing too little information when he is think chain.",
);
}
#[test]
fn very_less_time() {
assert_suggestion_result(
"Connection also takes very less time.",
VeryLess::default(),
"Connection also takes a lot less time.",
);
}
#[test]
fn too_less_space() {
assert_suggestion_result(
"Auto-abbreviate text if there is too less space",
VeryLess::default(),
"Auto-abbreviate text if there is too little space",
);
}
#[test]
fn cant_fix_is_quite_less() {
assert_suggestion_result(
"Based on the result, the number of genes in the braker.gtf from braker3 is quite less (25943) than braker2 (48396).",
VeryLess::default(),
"Based on the result, the number of genes in the braker.gtf from braker3 is quite a lot less (25943) than braker2 (48396).",
);
}
#[test]
fn cant_fix_become_quite_less() {
assert_suggestion_result(
"But those things became quite less important compared to having a consistent formatting style across a team",
VeryLess::default(),
"But those things became quite a lot less important compared to having a consistent formatting style across a team",
);
}
#[test]
fn dont_flag_comparably_less() {
assert_no_lints(
"This is comparably less retraining, but the incorrect keypresses are the bigger issue.",
VeryLess::default(),
);
}
#[test]
#[ignore = "May be beyond the typical Germlish usage? Requires 'very low/small' etc."]
fn cant_fix_very_less_ccr() {
assert_suggestion_result(
"[Bug]: Disabled primary button having very less color contrast ratio",
VeryLess::default(),
"[Bug]: Disabled primary button having very low color contrast ratio",
);
}
#[test]
#[ignore = "Too hard to parse. Does it mean the log is too small?"]
fn cant_fix_log_too_less() {
assert_no_lints(
"build error log too less to find the key position",
VeryLess::default(),
);
}
#[test]
#[ignore = "Proper fix would be something like 'pretty few items' or 'a pretty small number of items', etc."]
fn cant_fix_pretty_less() {
assert_suggestion_result(
"I have pretty less number of items in x-axis",
VeryLess::default(),
"I have pretty few items in x-axis",
);
}
}