Skip to content

Commit 3656602

Browse files
committed
feat: it´s→it's
1 parent 716bc50 commit 3656602

7 files changed

Lines changed: 196 additions & 128 deletions

File tree

harper-core/src/expr/sequence_expr.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,7 @@ impl SequenceExpr {
624624
gen_then_from_is!(hyphen);
625625
gen_then_from_is!(period);
626626
gen_then_from_is!(semicolon);
627+
gen_then_from_is!(acute);
627628
gen_then_from_is!(quote);
628629
gen_then_from_is!(backslash);
629630
gen_then_from_is!(slash);

harper-core/src/linting/lint_group.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ use super::right_click::RightClick;
177177
use super::roller_skated::RollerSkated;
178178
use super::safe_to_save::SafeToSave;
179179
use super::save_to_safe::SaveToSafe;
180-
use super::semicolon_apostrophe::SemicolonApostrophe;
181180
use super::sentence_capitalization::SentenceCapitalization;
182181
use super::shoot_oneself_in_the_foot::ShootOneselfInTheFoot;
183182
use super::simple_past_to_past_participle::SimplePastToPastParticiple;
@@ -235,6 +234,7 @@ use super::wish_could::WishCould;
235234
use super::wordpress_dotcom::WordPressDotcom;
236235
use super::worth_to_do::WorthToDo;
237236
use super::would_never_have::WouldNeverHave;
237+
use super::wrong_apostrophe::WrongApostrophe;
238238

239239
use super::{ExprLinter, Lint};
240240
use super::{HtmlDescriptionLinter, Linter};
@@ -578,7 +578,6 @@ impl LintGroup {
578578
insert_expr_rule!(RollerSkated, true);
579579
insert_expr_rule!(SafeToSave, true);
580580
insert_expr_rule!(SaveToSafe, true);
581-
insert_expr_rule!(SemicolonApostrophe, true);
582581
insert_expr_rule!(ShootOneselfInTheFoot, true);
583582
insert_expr_rule!(SimplePastToPastParticiple, true);
584583
insert_expr_rule!(SinceDuration, true);
@@ -603,6 +602,7 @@ impl LintGroup {
603602
insert_expr_rule!(ThenThan, true);
604603
insert_expr_rule!(Theres, true);
605604
insert_expr_rule!(ThesesThese, true);
605+
insert_struct_rule!(TheyreConfusions, true);
606606
insert_expr_rule!(ThingThink, true);
607607
insert_expr_rule!(ThisTypeOfThing, true);
608608
insert_expr_rule!(ThoughThought, true);
@@ -612,7 +612,6 @@ impl LintGroup {
612612
insert_struct_rule!(ToTwoToo, true);
613613
insert_expr_rule!(Touristic, true);
614614
insert_expr_rule!(TryOnesHandAt, true);
615-
insert_struct_rule!(TheyreConfusions, true);
616615
insert_struct_rule!(UnclosedQuotes, true);
617616
insert_expr_rule!(UpdatePlaceNames, true);
618617
insert_expr_rule!(VerbToAdjective, true);
@@ -631,6 +630,7 @@ impl LintGroup {
631630
insert_expr_rule!(WishCould, true);
632631
insert_struct_rule!(WordPressDotcom, true);
633632
insert_expr_rule!(WouldNeverHave, true);
633+
insert_expr_rule!(WrongApostrophe, true);
634634

635635
out.add("SpellCheck", SpellCheck::new(dictionary.clone(), dialect));
636636
out.config.set_rule_enabled("SpellCheck", true);

harper-core/src/linting/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@ mod right_click;
186186
mod roller_skated;
187187
mod safe_to_save;
188188
mod save_to_safe;
189-
mod semicolon_apostrophe;
190189
mod sentence_capitalization;
191190
mod shoot_oneself_in_the_foot;
192191
mod simple_past_to_past_participle;
@@ -245,6 +244,7 @@ mod wish_could;
245244
mod wordpress_dotcom;
246245
mod worth_to_do;
247246
mod would_never_have;
247+
mod wrong_apostrophe;
248248

249249
pub use expr_linter::{Chunk, ExprLinter};
250250
pub use initialism_linter::InitialismLinter;

harper-core/src/linting/semicolon_apostrophe.rs

Lines changed: 0 additions & 124 deletions
This file was deleted.
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
use crate::{
2+
Token, TokenStringExt,
3+
expr::{Expr, FirstMatchOf, SequenceExpr},
4+
linting::{ExprLinter, Lint, LintKind, Suggestion, expr_linter::Chunk},
5+
};
6+
7+
const CONTRACTION_AND_POSSESSIVE_ENDINGS: [&str; 7] = ["d", "ll", "m", "re", "s", "t", "ve"];
8+
9+
pub struct WrongApostrophe {
10+
expr: Box<dyn Expr>,
11+
}
12+
13+
impl Default for WrongApostrophe {
14+
fn default() -> Self {
15+
Self {
16+
expr: Box::new(FirstMatchOf::new(vec![
17+
Box::new(
18+
SequenceExpr::any_word()
19+
.then_semicolon()
20+
.then_word_set(&CONTRACTION_AND_POSSESSIVE_ENDINGS),
21+
),
22+
Box::new(
23+
SequenceExpr::any_word()
24+
.then_acute()
25+
.then_word_set(&CONTRACTION_AND_POSSESSIVE_ENDINGS),
26+
),
27+
])),
28+
}
29+
}
30+
}
31+
32+
impl ExprLinter for WrongApostrophe {
33+
type Unit = Chunk;
34+
35+
fn expr(&self) -> &dyn Expr {
36+
self.expr.as_ref()
37+
}
38+
39+
fn match_to_lint(&self, toks: &[Token], src: &[char]) -> Option<Lint> {
40+
let whole_span = toks.span()?;
41+
let base = &toks.first()?;
42+
let ending = &toks.last()?;
43+
44+
let replacement_str = format!(
45+
"{}'{}",
46+
base.span.get_content_string(src).to_lowercase(),
47+
ending.span.get_content_string(src).to_lowercase()
48+
);
49+
50+
let mut lettercase_template = base.span.get_content(src).to_vec();
51+
lettercase_template.extend_from_slice(ending.span.get_content(src));
52+
53+
Some(Lint {
54+
span: whole_span,
55+
lint_kind: LintKind::Typo,
56+
suggestions: vec![Suggestion::replace_with_match_case(
57+
replacement_str.chars().collect(),
58+
&lettercase_template,
59+
)],
60+
message: format!("Did you mean `{replacement_str}`?"),
61+
priority: 57,
62+
})
63+
}
64+
65+
fn description(&self) -> &str {
66+
"Corrects semicolons or acute accents typed instead of apostrophes."
67+
}
68+
}
69+
70+
#[cfg(test)]
71+
mod tests {
72+
use super::WrongApostrophe;
73+
use crate::linting::tests::{assert_lint_count, assert_suggestion_result};
74+
75+
#[test]
76+
fn fix_dont_with_semicolon_to_apostrophe() {
77+
assert_suggestion_result(
78+
"It's better if you don;t type like this.",
79+
WrongApostrophe::default(),
80+
"It's better if you don't type like this.",
81+
);
82+
}
83+
84+
#[test]
85+
fn ignore_correct() {
86+
assert_lint_count("I don't doubt it.", WrongApostrophe::default(), 0);
87+
}
88+
89+
#[test]
90+
fn fix_title_case() {
91+
assert_suggestion_result(
92+
"Don;t type like this.",
93+
WrongApostrophe::default(),
94+
"Don't type like this.",
95+
);
96+
}
97+
98+
#[test]
99+
fn fix_all_caps() {
100+
assert_suggestion_result(
101+
"DON;T TRY THIS AT HOME.",
102+
WrongApostrophe::default(),
103+
"DON'T TRY THIS AT HOME.",
104+
);
105+
}
106+
107+
#[test]
108+
#[ignore = "replace_with_match_case has a bug turning `I'll` into `I'LL`"]
109+
fn fix_ill_and_monkeys() {
110+
assert_suggestion_result(
111+
"Well I;ll be a monkey;s uncle!",
112+
WrongApostrophe::default(),
113+
"Well I'll be a monkey's uncle!",
114+
)
115+
}
116+
117+
#[test]
118+
fn fix_other_contractions_and_possessives() {
119+
assert_suggestion_result(
120+
"Let;s see if we;ve fixed patrakov;s bug. Fun wasn;t it?",
121+
WrongApostrophe::default(),
122+
"Let's see if we've fixed patrakov's bug. Fun wasn't it?",
123+
)
124+
}
125+
126+
#[test]
127+
fn corrects_ive_with_correct_capitalization() {
128+
assert_suggestion_result("I;ve", WrongApostrophe::default(), "I've");
129+
}
130+
131+
#[test]
132+
fn fix_acute_dont() {
133+
assert_suggestion_result(
134+
"To see the list of available bikes for a location, you don´t need any authentication.",
135+
WrongApostrophe::default(),
136+
"To see the list of available bikes for a location, you don't need any authentication.",
137+
);
138+
}
139+
140+
#[test]
141+
fn fix_acute_im() {
142+
assert_suggestion_result(
143+
"In my research, I´m applying the latest generation of quantitative methods in epidemiology",
144+
WrongApostrophe::default(),
145+
"In my research, I'm applying the latest generation of quantitative methods in epidemiology",
146+
);
147+
}
148+
149+
#[test]
150+
fn fix_acute_its() {
151+
assert_suggestion_result(
152+
"and it´s auto-updated if that project is hosted here on github",
153+
WrongApostrophe::default(),
154+
"and it's auto-updated if that project is hosted here on github",
155+
);
156+
}
157+
158+
#[test]
159+
fn fix_acute_lets() {
160+
assert_suggestion_result(
161+
"Let´s now visit the main functionalities provided by GrimoireLab.",
162+
WrongApostrophe::default(),
163+
"Let's now visit the main functionalities provided by GrimoireLab.",
164+
);
165+
}
166+
167+
#[test]
168+
fn fix_acute_microsofts() {
169+
assert_suggestion_result(
170+
"Windows 11 Upgrade tool that bypasses new Microsoft´s requirements",
171+
WrongApostrophe::default(),
172+
"Windows 11 Upgrade tool that bypasses new Microsoft's requirements",
173+
);
174+
}
175+
176+
#[test]
177+
fn fix_acute_youre() {
178+
assert_suggestion_result(
179+
"You´re looking for clues, but you´re missing all the signs",
180+
WrongApostrophe::default(),
181+
"You're looking for clues, but you're missing all the signs",
182+
);
183+
}
184+
}

harper-core/src/punctuation.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ pub enum Punctuation {
7878
Pipe,
7979
/// `_`
8080
Underscore,
81+
/// `´`
82+
Acute,
8183
}
8284

8385
impl Punctuation {
@@ -119,6 +121,7 @@ impl Punctuation {
119121
'+' => Punctuation::Plus,
120122
'|' => Punctuation::Pipe,
121123
'_' => Punctuation::Underscore,
124+
'´' => Punctuation::Acute,
122125
_ => Punctuation::Currency(Currency::from_char(c)?),
123126
};
124127

0 commit comments

Comments
 (0)