harper_core/linting/
pique_interest.rs1use crate::{
2 CharString, CharStringExt, Token,
3 char_string::char_string,
4 patterns::{Pattern, SequencePattern, WordSet},
5};
6
7use super::{Lint, LintKind, PatternLinter, Suggestion};
8
9pub struct PiqueInterest {
10 pattern: Box<dyn Pattern>,
11}
12
13impl Default for PiqueInterest {
14 fn default() -> Self {
15 let pattern = SequencePattern::default()
16 .then(WordSet::new(&[
17 "peak", "peaked", "peek", "peeked", "peeking", "peaking",
18 ]))
19 .then_whitespace()
20 .then_not_plural_nominal()
21 .then_whitespace()
22 .t_aco("interest");
23
24 Self {
25 pattern: Box::new(pattern),
26 }
27 }
28}
29
30impl PiqueInterest {
31 fn to_correct(word: &str) -> Option<CharString> {
32 Some(match word.to_lowercase().as_str() {
33 "peak" => char_string!("pique"),
34 "peek" => char_string!("pique"),
35 "peeked" => char_string!("piqued"),
36 "peaked" => char_string!("piqued"),
37 "peaking" => char_string!("piquing"),
38 "peeking" => char_string!("piquing"),
39 _ => return None,
40 })
41 }
42}
43
44impl PatternLinter for PiqueInterest {
45 fn pattern(&self) -> &dyn Pattern {
46 self.pattern.as_ref()
47 }
48
49 fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Option<Lint> {
50 let span = matched_tokens[0].span;
51 let word = span.get_content_string(source).to_lowercase();
52 let correct = Self::to_correct(&word)?;
53
54 Some(Lint {
55 span,
56 lint_kind: LintKind::WordChoice,
57 suggestions: vec![Suggestion::replace_with_match_case(
58 correct.to_vec(),
59 matched_tokens[0].span.get_content(source),
60 )],
61 message: format!(
62 "Did you mean `{}` instead of `{}`?",
63 correct.to_string(),
64 word,
65 ),
66 priority: 31,
67 })
68 }
69
70 fn description(&self) -> &'static str {
71 "Detects incorrect usage of `peak` or `peek` when the intended word is `pique`, as in the phrase `you've peaked my interest`."
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::PiqueInterest;
78 use crate::linting::tests::assert_suggestion_result;
79
80 #[test]
81 fn corrects_peak_interest() {
82 assert_suggestion_result(
83 "The story managed to peak his interest.",
84 PiqueInterest::default(),
85 "The story managed to pique his interest.",
86 );
87 }
88
89 #[test]
90 fn corrects_peeked_interest_at_start() {
91 assert_suggestion_result(
92 "Peeked his interest, did she?",
93 PiqueInterest::default(),
94 "Piqued his interest, did she?",
95 );
96 }
97
98 #[test]
99 fn corrects_peak_interest_in_middle() {
100 assert_suggestion_result(
101 "She tried to peak his interest during the lecture.",
102 PiqueInterest::default(),
103 "She tried to pique his interest during the lecture.",
104 );
105 }
106
107 #[test]
108 fn corrects_peaked_interest_at_end() {
109 assert_suggestion_result(
110 "All along, she hoped she peaked his interest.",
111 PiqueInterest::default(),
112 "All along, she hoped she piqued his interest.",
113 );
114 }
115
116 #[test]
117 fn does_not_correct_unrelated_peak() {
118 assert_suggestion_result(
119 "He reached the peak of the mountain.",
120 PiqueInterest::default(),
121 "He reached the peak of the mountain.",
122 );
123 }
124
125 #[test]
126 fn corrects_peaking_interest() {
127 assert_suggestion_result(
128 "She was peaking his interest with her stories.",
129 PiqueInterest::default(),
130 "She was piquing his interest with her stories.",
131 );
132 }
133}