use crate::{
Token, TokenKind,
expr::{Expr, SequenceExpr},
linting::{
ExprLinter, Lint, LintKind, Suggestion,
expr_linter::{Chunk, preceded_by_word},
},
};
pub struct PossessiveYour {
expr: SequenceExpr,
}
impl Default for PossessiveYour {
fn default() -> Self {
let pattern = SequenceExpr::aco("you")
.then_whitespace()
.then_kind_is_but_is_not_except(
TokenKind::is_nominal,
TokenKind::is_likely_homograph,
&["guys", "what's"],
);
Self { expr: pattern }
}
}
impl ExprLinter for PossessiveYour {
type Unit = Chunk;
fn expr(&self) -> &dyn Expr {
&self.expr
}
fn match_to_lint_with_context(
&self,
matched_tokens: &[Token],
source: &[char],
ctx: Option<(&[Token], &[Token])>,
) -> Option<Lint> {
if preceded_by_word(ctx, |pw| pw.kind.is_verb()) {
return None;
}
let span = matched_tokens.first()?.span;
let orig_chars = span.get_content(source);
Some(Lint {
span,
lint_kind: LintKind::WordChoice,
suggestions: vec![
Suggestion::replace_with_match_case("your".chars().collect(), orig_chars),
Suggestion::replace_with_match_case("you're a".chars().collect(), orig_chars),
Suggestion::replace_with_match_case("you're an".chars().collect(), orig_chars),
],
message: "The possessive version of this word is more common in this context."
.to_owned(),
..Default::default()
})
}
fn description(&self) -> &'static str {
"The possessive form of `you` is more likely before nouns."
}
}
#[cfg(test)]
mod tests {
use crate::linting::tests::{assert_lint_count, assert_no_lints, assert_suggestion_result};
use super::PossessiveYour;
#[test]
#[ignore = "currently fails because comments is a homographs (verb or noun)"]
fn your_comments() {
assert_suggestion_result(
"You comments may end up in the documentation.",
PossessiveYour::default(),
"Your comments may end up in the documentation.",
);
}
#[test]
fn allow_intro_page() {
assert_lint_count(
"You can try out an editor that uses Harper under-the-hood here.",
PossessiveYour::default(),
0,
);
}
#[test]
fn allow_you_guys() {
assert_lint_count(
"I mean I'm pretty sure you guys can't do anything with this stuff.",
PossessiveYour::default(),
0,
);
}
#[test]
fn test_suggestion_your() {
assert_suggestion_result(
"You combination of artist and teacher.",
PossessiveYour::default(),
"Your combination of artist and teacher.",
);
}
#[test]
fn test_suggestion_youre_a() {
assert_suggestion_result(
"You combination of artist and teacher.",
PossessiveYour::default(),
"You're a combination of artist and teacher.",
);
}
#[test]
#[ignore]
fn test_suggestion_multiple() {
assert_suggestion_result(
"You knowledge. You imagination. You icosahedron",
PossessiveYour::default(),
"Your knowledge. Your imagination. You're an icosahedron",
);
}
#[test]
fn dont_flag_just_showing_you() {
assert_lint_count(
"I'm just showing you what's available and how to use it.",
PossessiveYour::default(),
0,
);
}
#[test]
fn allows_issue_1583() {
assert_no_lints(
"Note that in a world with modules everywhere, you almost never need an IIFE",
PossessiveYour::default(),
);
}
#[test]
fn dont_flag_1919_brought_you() {
assert_lint_count(
"team who also brought you [BloodHound Enterprise](http://specterops.io/bloodhound-verview/).",
PossessiveYour::default(),
0,
);
}
#[test]
fn dont_flag_1919_teaches_you() {
assert_lint_count(
"Teaches you PyTorch and many machine learning concepts in a hands-on, code-first way.",
PossessiveYour::default(),
0,
);
}
}