Skip to content

Commit 4637033

Browse files
authored
feat:by my own→on my own/by myself (#3322)
1 parent f631dc0 commit 4637033

4 files changed

Lines changed: 368 additions & 0 deletions

File tree

harper-core/default_config.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3962,6 +3962,13 @@
39623962
"label": "Bought"
39633963
}
39643964
},
3965+
{
3966+
"Bool": {
3967+
"name": "ByOnesOwn",
3968+
"state": true,
3969+
"label": "By One's Own"
3970+
}
3971+
},
39653972
{
39663973
"Bool": {
39673974
"name": "CallThem",
Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
use crate::{
2+
Lint, Token, TokenStringExt,
3+
char_string::CharStringExt,
4+
expr::{Expr, SequenceExpr},
5+
linting::{
6+
ExprLinter, LintKind, Suggestion,
7+
expr_linter::{Chunk, followed_by_word},
8+
},
9+
};
10+
11+
pub struct ByOnesOwn {
12+
expr: SequenceExpr,
13+
}
14+
15+
impl Default for ByOnesOwn {
16+
fn default() -> Self {
17+
Self {
18+
expr: SequenceExpr::aco("by")
19+
.t_ws()
20+
.then_possessive_determiner()
21+
.t_ws()
22+
.t_aco("own"),
23+
}
24+
}
25+
}
26+
27+
impl ExprLinter for ByOnesOwn {
28+
type Unit = Chunk;
29+
30+
fn match_to_lint_with_context(
31+
&self,
32+
toks: &[Token],
33+
src: &[char],
34+
ctx: Option<(&[Token], &[Token])>,
35+
) -> Option<Lint> {
36+
// If any word comes next then "own" is probably a qualifier.
37+
if followed_by_word(ctx, |_| true) {
38+
return None;
39+
}
40+
41+
let span = toks.span()?;
42+
43+
let (by_idx, ws1_idx, whose_idx) = (0, 1, 2);
44+
45+
let by_ws = toks[by_idx..=ws1_idx].span()?.get_content(src);
46+
let ws_1 = toks[ws1_idx].get_ch(src);
47+
48+
// my, its
49+
let whose = toks[whose_idx].get_ch(src);
50+
// my own, its own
51+
let whose_own = toks[whose_idx..].span()?.get_content(src);
52+
53+
// my -> myself, its -> itself
54+
let reflexive = match whose {
55+
_ if whose.eq_ch(&['m', 'y']) => &["myself"][..],
56+
_ if whose.eq_ch(&['o', 'u', 'r']) => &["ourselves"][..],
57+
_ if whose.eq_ch(&['y', 'o', 'u', 'r']) => &["yourself", "yourselves"][..],
58+
_ if whose.eq_ch(&['h', 'i', 's']) => &["himself"][..],
59+
_ if whose.eq_ch(&['h', 'e', 'r']) => &["herself"][..],
60+
_ if whose.eq_ch(&['i', 't', 's']) => &["itself"][..],
61+
_ if whose.eq_ch(&['t', 'h', 'e', 'i', 'r']) => &["themselves", "themself"][..],
62+
_ if whose.eq_ch(&['o', 'n', 'e', '\'', 's']) => &["oneself"][..],
63+
_ => return None, // Actually unreachable
64+
};
65+
66+
let sugg_on_ones_own = Suggestion::replace_with_match_case(
67+
"on".chars()
68+
.chain(ws_1.iter().copied())
69+
.chain(whose_own.iter().copied())
70+
.collect::<Vec<char>>(),
71+
span.get_content(src),
72+
);
73+
74+
let suggestions = std::iter::once(sugg_on_ones_own)
75+
.chain(reflexive.iter().map(|reflexive_str| {
76+
let by_whoseself = by_ws
77+
.iter()
78+
.copied()
79+
.chain(reflexive_str.chars())
80+
.collect::<Vec<char>>();
81+
Suggestion::replace_with_match_case(by_whoseself, span.get_content(src))
82+
}))
83+
.collect();
84+
85+
Some(Lint {
86+
span,
87+
lint_kind: LintKind::Usage,
88+
suggestions,
89+
message:
90+
"`By one's own` is not idiomatic. Consider either `on one's own` or `by oneself`."
91+
.to_string(),
92+
..Default::default()
93+
})
94+
}
95+
96+
fn expr(&self) -> &dyn Expr {
97+
&self.expr
98+
}
99+
100+
fn description(&self) -> &str {
101+
"Fixes incorrect phrases like `by my own` by suggesting `on my own` or `by myself`."
102+
}
103+
}
104+
105+
#[cfg(test)]
106+
mod tests {
107+
use crate::linting::tests::{assert_good_and_bad_suggestions, assert_no_lints};
108+
109+
use super::ByOnesOwn;
110+
111+
#[test]
112+
fn fix_by_her_own() {
113+
assert_good_and_bad_suggestions(
114+
"If she is not managing by her own, it means that there is something wrong with either our documentation, or our error messages, or our ...",
115+
ByOnesOwn::default(),
116+
&[
117+
"If she is not managing on her own, it means that there is something wrong with either our documentation, or our error messages, or our ...",
118+
"If she is not managing by herself, it means that there is something wrong with either our documentation, or our error messages, or our ...",
119+
],
120+
&[],
121+
);
122+
}
123+
124+
#[test]
125+
fn dont_flag_by_her_breathing() {
126+
assert_no_lints(
127+
"The silence of this deep section of the Spire was profound, broken only by her own ragged breathing.",
128+
ByOnesOwn::default(),
129+
);
130+
}
131+
132+
#[test]
133+
fn fix_by_his_own() {
134+
assert_good_and_bad_suggestions(
135+
"And make the hover info function creating the <title> tag by his own.",
136+
ByOnesOwn::default(),
137+
&[
138+
"And make the hover info function creating the <title> tag on his own.",
139+
"And make the hover info function creating the <title> tag by himself.",
140+
],
141+
&[],
142+
);
143+
}
144+
145+
#[test]
146+
fn fix_by_his_own_comma() {
147+
assert_good_and_bad_suggestions(
148+
"it also makes a report all by his own, without touching anything",
149+
ByOnesOwn::default(),
150+
&[
151+
"it also makes a report all on his own, without touching anything",
152+
"it also makes a report all by himself, without touching anything",
153+
],
154+
&[],
155+
);
156+
}
157+
158+
#[test]
159+
fn dont_flag_by_his_own_efforts() {
160+
assert_no_lints(
161+
"My point is if someone needs jemalloc he gets it by his own efforts clearly realising why/when and so on.",
162+
ByOnesOwn::default(),
163+
);
164+
}
165+
166+
#[test]
167+
fn fix_by_its_own_in_quotes() {
168+
assert_good_and_bad_suggestions(
169+
"I set AdGuard DNS via Windows 11 settings, and I always found them to be changed \"by its own\".",
170+
ByOnesOwn::default(),
171+
&[
172+
"I set AdGuard DNS via Windows 11 settings, and I always found them to be changed \"on its own\".",
173+
"I set AdGuard DNS via Windows 11 settings, and I always found them to be changed \"by itself\".",
174+
],
175+
&[],
176+
);
177+
}
178+
179+
#[test]
180+
fn fix_by_its_own_at_end() {
181+
assert_good_and_bad_suggestions(
182+
"The wheel starts speeding and spinning by its own.",
183+
ByOnesOwn::default(),
184+
&[
185+
"The wheel starts speeding and spinning on its own.",
186+
"The wheel starts speeding and spinning by itself.",
187+
],
188+
&[],
189+
);
190+
}
191+
192+
#[test]
193+
fn dont_flag_by_its_own_changes() {
194+
assert_no_lints("Remix is spooked by its own changes", ByOnesOwn::default());
195+
}
196+
197+
#[test]
198+
fn fix_by_my_own() {
199+
assert_good_and_bad_suggestions(
200+
"how can I build a project by my own",
201+
ByOnesOwn::default(),
202+
&[
203+
"how can I build a project on my own",
204+
"how can I build a project by myself",
205+
],
206+
&[],
207+
);
208+
}
209+
210+
#[test]
211+
fn dont_flag_by_my_own_hr() {
212+
assert_no_lints(
213+
"My colledge' ID is totally messed up by my own HR",
214+
ByOnesOwn::default(),
215+
);
216+
}
217+
218+
#[test]
219+
fn fix_by_our_own() {
220+
assert_good_and_bad_suggestions(
221+
"So, I think we should have the alternative BN implementation by our own.",
222+
ByOnesOwn::default(),
223+
&[
224+
"So, I think we should have the alternative BN implementation on our own.",
225+
"So, I think we should have the alternative BN implementation by ourselves.",
226+
],
227+
&[],
228+
);
229+
}
230+
231+
#[test]
232+
fn dont_flag_by_our_own_standards() {
233+
assert_no_lints("Better by our own standards.", ByOnesOwn::default());
234+
}
235+
236+
#[test]
237+
fn fix_by_their_own() {
238+
assert_good_and_bad_suggestions(
239+
"Therefore, it would be great to allow the users to do the serialization by their own, rather than giving the full control to the SDK internally.",
240+
ByOnesOwn::default(),
241+
&[
242+
"Therefore, it would be great to allow the users to do the serialization on their own, rather than giving the full control to the SDK internally.",
243+
"Therefore, it would be great to allow the users to do the serialization by themselves, rather than giving the full control to the SDK internally.",
244+
],
245+
&[],
246+
);
247+
}
248+
249+
#[test]
250+
fn dont_flag_by_their_own_bots() {
251+
assert_no_lints(
252+
"GitHub needs a way to report issues and comments, even by their own bots.",
253+
ByOnesOwn::default(),
254+
);
255+
}
256+
257+
#[test]
258+
fn fix_by_your_own() {
259+
assert_good_and_bad_suggestions(
260+
"You should be able to see a new wallpaper slideshow created by your own.",
261+
ByOnesOwn::default(),
262+
&[
263+
"You should be able to see a new wallpaper slideshow created on your own.",
264+
"You should be able to see a new wallpaper slideshow created by yourself.",
265+
],
266+
&[],
267+
);
268+
}
269+
270+
#[test]
271+
fn dont_flag_by_your_own_lib() {
272+
assert_no_lints(
273+
"Design with JSX, powered by your own component library.",
274+
ByOnesOwn::default(),
275+
);
276+
}
277+
278+
// For future contributors who want to improve this linter
279+
280+
#[test]
281+
#[ignore = "We can't detect the end of sentence when punctuation is missing."]
282+
fn dont_flag_by_its_own_end_no_punc() {
283+
assert_no_lints(
284+
"same infininte running and when i stop this node forcefully it show s the currect output dont know why it dont stop by its own i think its a bug",
285+
ByOnesOwn::default(),
286+
);
287+
}
288+
289+
#[test]
290+
#[ignore = "We might be able to handle this one"]
291+
fn fix_by_your_own_and() {
292+
assert_good_and_bad_suggestions(
293+
"After you install SPAdes by your own and add spades bin to the $PATH, ",
294+
ByOnesOwn::default(),
295+
&[
296+
"After you install SPAdes on your own and add spades bin to the $PATH, ",
297+
"After you install SPAdes by yourself and add spades bin to the $PATH, ",
298+
],
299+
&[],
300+
);
301+
}
302+
303+
#[test]
304+
#[ignore = "A construct that needs to be fixed that we can't handle yet"]
305+
fn fix_by_your_own_using() {
306+
assert_good_and_bad_suggestions(
307+
"I made some steps to this direction and currently you can implement it by your own using middleware",
308+
ByOnesOwn::default(),
309+
&[
310+
"I made some steps in this direction and currently you can implement it on your own using middleware",
311+
"I made some steps in this direction and currently you can implement it by yourself using middleware",
312+
],
313+
&[],
314+
);
315+
}
316+
317+
#[test]
318+
#[ignore = "We might be able to handle this"]
319+
fn fix_by_your_own_preposition() {
320+
assert_good_and_bad_suggestions(
321+
"Of course feel free to add this by your own by pull request :)",
322+
ByOnesOwn::default(),
323+
&[
324+
"Of course feel free to add this on your own by pull request :)",
325+
"Of course feel free to add this by yourself by pull request :)",
326+
],
327+
&[],
328+
);
329+
}
330+
331+
#[test]
332+
#[ignore = "Another construction we should fix but can't yet"]
333+
fn fix_by_your_own_we() {
334+
assert_good_and_bad_suggestions(
335+
"If you would like to develop this feature by your own we would be happy to help with this but",
336+
ByOnesOwn::default(),
337+
&[
338+
"If you would like to develop this feature on your own we would be happy to help with this but",
339+
"If you would like to develop this feature by yourself we would be happy to help with this but",
340+
],
341+
&[],
342+
);
343+
}
344+
345+
#[test]
346+
#[ignore = "See if it's always a legit error when the next word is a preposition"]
347+
fn fix_by_her_own_during() {
348+
assert_good_and_bad_suggestions(
349+
"most of the time she gets mad by her own during topics which can end her in an angry/annoyed mood",
350+
ByOnesOwn::default(),
351+
&[
352+
"most of the time she gets mad on her own during topics which can end her in an angry/annoyed mood",
353+
"most of the time she gets mad by herself during topics which can end her in an angry/annoyed mood",
354+
],
355+
&[],
356+
);
357+
}
358+
}

harper-core/src/linting/lint_group/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ use super::boring_words::BoringWords;
4242
use super::bought::Bought;
4343
use super::brand_brandish::BrandBrandish;
4444
use super::by_accident::ByAccident;
45+
use super::by_ones_own::ByOnesOwn;
4546
use super::by_the_book::ByTheBook;
4647
use super::call_them::CallThem;
4748
use super::cant::Cant;
@@ -617,6 +618,7 @@ impl LintGroup {
617618
insert_expr_rule!(Bought, true);
618619
insert_expr_rule!(BrandBrandish, true);
619620
insert_expr_rule!(ByAccident, true);
621+
insert_expr_rule!(ByOnesOwn, true);
620622
insert_expr_rule!(ByTheBook, true);
621623
insert_expr_rule!(CallThem, true);
622624
insert_expr_rule!(Cant, true);

harper-core/src/linting/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ mod boring_words;
3535
mod bought;
3636
mod brand_brandish;
3737
mod by_accident;
38+
mod by_ones_own;
3839
mod by_the_book;
3940
mod call_them;
4041
mod cant;

0 commit comments

Comments
 (0)