forked from Automattic/harper
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patholdest_in_the_book.rs
More file actions
168 lines (144 loc) · 5.57 KB
/
Copy patholdest_in_the_book.rs
File metadata and controls
168 lines (144 loc) · 5.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
use crate::{
CharStringExt, Lint, Token,
expr::{Expr, Repeating, SequenceExpr},
linting::{ExprLinter, LintKind, Suggestion, expr_linter::Chunk},
};
pub struct OldestInTheBook {
expr: SequenceExpr,
}
impl Default for OldestInTheBook {
fn default() -> Self {
let adj = |t: &Token, s: &[char]| {
let k = &t.kind;
(k.is_np_member() || k.is_adjective())
&& !k.is_noun()
&& !t.get_ch(s).eq_ch(&['i', 'n'])
};
// Zero or more adjectives
let adjseq = Repeating::new(Box::new(SequenceExpr::with(adj).t_ws()), 0);
let noun = |t: &Token, s: &[char]| {
let k = &t.kind;
(k.is_np_member() || k.is_noun() || k.is_oov()) && !t.get_ch(s).eq_ch(&['i', 'n'])
};
// One or more nouns
let nounseq = SequenceExpr::with(noun).then_optional(Repeating::new(
Box::new(SequenceExpr::default().t_ws().then(noun)),
1,
));
let noun_phrase = SequenceExpr::optional(adjseq).then(nounseq);
Self {
expr: SequenceExpr::fixed_phrase("oldest ")
.then(noun_phrase)
.t_ws()
.then_word_seq(&["in", "the", "books"]),
}
}
}
impl ExprLinter for OldestInTheBook {
type Unit = Chunk;
fn expr(&self) -> &dyn Expr {
&self.expr
}
fn match_to_lint_with_context(
&self,
toks: &[Token],
src: &[char],
_ctx: Option<(&[Token], &[Token])>,
) -> Option<Lint> {
let np = &toks[2..toks.len() - 4];
let tricky = np.iter().any(|n| {
n.get_ch(src)
.eq_any_ignore_ascii_case_str(&["trick", "tricks"])
});
let message = if tricky {
"This idiom should use singular `book` instead of plural `books`."
} else {
"If this is a play on the idiom `oldest trick in the book`, it should use singular `book` instead of plural `books`."
}
.to_string();
Some(Lint {
span: toks.last()?.span,
lint_kind: LintKind::Usage,
suggestions: vec![Suggestion::replace_with_match_case_str(
"book",
toks.last()?.get_ch(src),
)],
message,
..Default::default()
})
}
fn description(&self) -> &'static str {
"Detects the idiom `oldest X in the books`, which should use singular `book`."
}
}
#[cfg(test)]
mod tests {
use super::OldestInTheBook;
use crate::linting::tests::{assert_lint_message, assert_suggestion_result};
// Probable references to the idiom "oldest trick in the book"
#[test]
fn fix_delphi_mistake() {
assert_suggestion_result(
"This is the oldest Delphi mistake in the books and I'm sure you've made it before (we all have), and I'm sure you recognise it when you see it.",
OldestInTheBook::default(),
"This is the oldest Delphi mistake in the book and I'm sure you've made it before (we all have), and I'm sure you recognise it when you see it.",
);
}
#[test]
fn fix_trick() {
assert_suggestion_result(
"... oldest trick in the books, a restart and it works all the times(for now).",
OldestInTheBook::default(),
"... oldest trick in the book, a restart and it works all the times(for now).",
);
}
#[test]
fn fix_virus_trick() {
assert_suggestion_result(
"Once the OS is started the MBR is typically protected for virus reasons - this is one of the oldest virus tricks in the books - goes back to ...",
OldestInTheBook::default(),
"Once the OS is started the MBR is typically protected for virus reasons - this is one of the oldest virus tricks in the book - goes back to ...",
)
}
#[test]
fn fix_mistake() {
assert_suggestion_result(
"Ok, I realized now that I was making the oldest mistake in the books with my code, dividing my v by 2 instead of dividing it by 5.",
OldestInTheBook::default(),
"Ok, I realized now that I was making the oldest mistake in the book with my code, dividing my v by 2 instead of dividing it by 5.",
);
}
#[test]
fn fix_tricks() {
assert_suggestion_result(
"He enables the oldest tricks in the books, create fear from thing like prosperity (we really don't need Foxconn?)",
OldestInTheBook::default(),
"He enables the oldest tricks in the book, create fear from thing like prosperity (we really don't need Foxconn?)",
);
}
#[test]
fn fix_military_plays() {
assert_suggestion_result(
"Isnt that like one of the oldest military plays in the books?",
OldestInTheBook::default(),
"Isnt that like one of the oldest military plays in the book?",
);
}
// Test messages
#[test]
fn is_oldest_trick_in_the_books_ref_to_idom() {
assert_lint_message(
"This is one of the oldest trick in the books",
OldestInTheBook::default(),
"This idiom should use singular `book` instead of plural `books`.",
);
}
#[test]
fn is_chromatic_alterations_ref_to_idom() {
assert_lint_message(
"One of the oldest chromatic alterations in the books is the raising of the leading tone",
OldestInTheBook::default(),
"If this is a play on the idiom `oldest trick in the book`, it should use singular `book` instead of plural `books`.",
);
}
}