use harper_brill::UPOS;
use crate::linting::expr_linter::Chunk;
use crate::{
CharStringExt, Token, TokenKind,
expr::{Expr, SequenceExpr},
linting::{ExprLinter, Lint, LintKind, Suggestion},
patterns::UPOSSet,
};
pub struct WereWhere {
expr: SequenceExpr,
}
impl Default for WereWhere {
fn default() -> Self {
let unambiguous_pronoun_where = SequenceExpr::word_set(&["they", "we"])
.t_ws()
.t_aco("where");
let you_where_verb = SequenceExpr::aco("you")
.t_ws()
.t_aco("where")
.t_ws()
.then(UPOSSet::new(&[UPOS::VERB, UPOS::AUX, UPOS::ADJ]));
let verb_were_clause =
SequenceExpr::with(|tok: &Token, _: &[char]| tok.kind.is_upos(UPOS::VERB))
.t_ws()
.t_aco("were")
.t_ws()
.then(UPOSSet::new(&[UPOS::PRON, UPOS::DET, UPOS::PROPN]));
Self {
expr: SequenceExpr::any_of(vec![
Box::new(unambiguous_pronoun_where),
Box::new(you_where_verb),
Box::new(verb_were_clause),
]),
}
}
}
impl ExprLinter for WereWhere {
type Unit = Chunk;
fn expr(&self) -> &dyn Expr {
&self.expr
}
fn match_to_lint(&self, toks: &[Token], src: &[char]) -> Option<Lint> {
const WHERE: &[char] = &['w', 'h', 'e', 'r', 'e'];
const WERE: &[char] = &['w', 'e', 'r', 'e'];
let where_tok = toks.iter().find(|tok| {
matches!(tok.kind, TokenKind::Word(_)) && tok.span.get_content(src).eq_ch(WHERE)
});
let were_tok = toks.iter().find(|tok| {
matches!(tok.kind, TokenKind::Word(_)) && tok.span.get_content(src).eq_ch(WERE)
});
if let Some(tok) = where_tok {
Some(Lint {
span: tok.span,
lint_kind: LintKind::Typo,
suggestions: vec![Suggestion::replace_with_match_case_str(
"were",
tok.span.get_content(src),
)],
message: "It looks like this is a typo, did you mean `were`?".to_string(),
..Default::default()
})
} else {
were_tok.map(|tok| Lint {
span: tok.span,
lint_kind: LintKind::Typo,
suggestions: vec![Suggestion::replace_with_match_case_str(
"where",
tok.span.get_content(src),
)],
message: "It looks like this is a typo, did you mean `where`?".to_string(),
..Default::default()
})
}
}
fn description(&self) -> &'static str {
"Detects mixing up `were` and `where`."
}
}
#[cfg(test)]
mod tests {
use super::WereWhere;
use crate::linting::tests::{assert_no_lints, assert_suggestion_result};
#[test]
fn fix_they_where() {
assert_suggestion_result(
"They where going to the store.",
WereWhere::default(),
"They were going to the store.",
);
}
#[test]
fn fix_we_where() {
assert_suggestion_result(
"We where right about that.",
WereWhere::default(),
"We were right about that.",
);
}
#[test]
fn fix_they_where_happy() {
assert_suggestion_result(
"They where happy with the result.",
WereWhere::default(),
"They were happy with the result.",
);
}
#[test]
fn fix_you_where_going() {
assert_suggestion_result(
"you where going in the right direction.",
WereWhere::default(),
"you were going in the right direction.",
);
}
#[test]
fn fix_you_where_right() {
assert_suggestion_result(
"you where right about that.",
WereWhere::default(),
"you were right about that.",
);
}
#[test]
fn fix_know_were_they() {
assert_suggestion_result(
"Do you know were they went?",
WereWhere::default(),
"Do you know where they went?",
);
}
#[test]
fn fix_forgot_were_i() {
assert_suggestion_result(
"I forgot were I put my keys.",
WereWhere::default(),
"I forgot where I put my keys.",
);
}
#[test]
fn fix_found_were_the() {
assert_suggestion_result(
"I found were the book was.",
WereWhere::default(),
"I found where the book was.",
);
}
#[test]
fn fix_go_were_they() {
assert_suggestion_result(
"Go were they tell you.",
WereWhere::default(),
"Go where they tell you.",
);
}
#[test]
fn fix_we_where_almost_done() {
assert_suggestion_result(
"We where almost done with the task.",
WereWhere::default(),
"We were almost done with the task.",
);
}
#[test]
fn fix_they_where_able() {
assert_suggestion_result(
"They where able to fix the issue in time.",
WereWhere::default(),
"They were able to fix the issue in time.",
);
}
#[test]
fn fix_we_where_told() {
assert_suggestion_result(
"We where told about the change last week.",
WereWhere::default(),
"We were told about the change last week.",
);
}
#[test]
fn fix_they_where_supposed() {
assert_suggestion_result(
"They where supposed to be here by now.",
WereWhere::default(),
"They were supposed to be here by now.",
);
}
#[test]
fn fix_you_where_supposed() {
assert_suggestion_result(
"You where supposed to call me.",
WereWhere::default(),
"You were supposed to call me.",
);
}
#[test]
fn fix_you_where_asked() {
assert_suggestion_result(
"you where asked to leave the room.",
WereWhere::default(),
"you were asked to leave the room.",
);
}
#[test]
fn fix_remember_were_i() {
assert_suggestion_result(
"Do you remember were I left the keys?",
WereWhere::default(),
"Do you remember where I left the keys?",
);
}
#[test]
fn fix_check_were_the() {
assert_suggestion_result(
"Check were the error occurred.",
WereWhere::default(),
"Check where the error occurred.",
);
}
#[test]
fn fix_asked_were_he() {
assert_suggestion_result(
"She asked were he lived.",
WereWhere::default(),
"She asked where he lived.",
);
}
#[test]
fn fix_know_were_the_bug() {
assert_suggestion_result(
"I know were the bug is.",
WereWhere::default(),
"I know where the bug is.",
);
}
#[test]
fn fix_find_were_it() {
assert_suggestion_result(
"Find were it crashed.",
WereWhere::default(),
"Find where it crashed.",
);
}
#[test]
fn no_flag_where_they_are() {
assert_no_lints("Do you know where they are going?", WereWhere::default());
}
#[test]
fn no_flag_they_were_going() {
assert_no_lints("They were going to the store.", WereWhere::default());
}
#[test]
fn no_flag_we_were_right() {
assert_no_lints("We were right about that.", WereWhere::default());
}
#[test]
fn no_flag_show_you_where() {
assert_no_lints("I'll show you where to go.", WereWhere::default());
}
#[test]
fn no_flag_tell_you_where_the() {
assert_no_lints("I'll tell you where the exit is.", WereWhere::default());
}
#[test]
fn no_flag_they_were_wrong() {
assert_no_lints("I think they were wrong.", WereWhere::default());
}
#[test]
fn no_flag_confirmed_they_were() {
assert_no_lints("I confirmed they were correct.", WereWhere::default());
}
#[test]
fn no_flag_found_they_were() {
assert_no_lints("He found they were missing.", WereWhere::default());
}
#[test]
fn no_flag_where_were_they() {
assert_no_lints("Where were they going?", WereWhere::default());
}
#[test]
fn no_flag_showed_me_where() {
assert_no_lints("He showed me where the exit was.", WereWhere::default());
}
#[test]
#[ignore = "limitation: 'you where' followed by DET is not flagged; would need DET in the following-word set"]
fn fix_you_where_the_only_one() {
assert_suggestion_result(
"you where the only one there.",
WereWhere::default(),
"you were the only one there.",
);
}
#[test]
#[ignore = "limitation: sentence-initial 'Where' as typo for 'Were' is not handled"]
fn fix_where_they_going_sentence_start() {
assert_suggestion_result(
"Where they going to the party?",
WereWhere::default(),
"Were they going to the party?",
);
}
#[test]
#[ignore = "limitation: indirect object between verb and 'were' is not detected"]
fn fix_showed_me_were() {
assert_suggestion_result(
"He showed me were the exit was.",
WereWhere::default(),
"He showed me where the exit was.",
);
}
}