Skip to content

Commit a50a6b2

Browse files
committed
refactor(core): separated types into their own modules
1 parent 8212d58 commit a50a6b2

File tree

6 files changed

+160
-148
lines changed

6 files changed

+160
-148
lines changed

harper-core/src/linting/lint.rs

+2-144
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
use std::fmt::Display;
2-
3-
use is_macro::Is;
41
use serde::{Deserialize, Serialize};
52

63
use crate::Span;
74

5+
use super::{LintKind, Suggestion};
6+
87
#[derive(Debug, Clone, Serialize, Deserialize)]
98
pub struct Lint {
109
pub span: Span,
@@ -27,144 +26,3 @@ impl Default for Lint {
2726
}
2827
}
2928
}
30-
31-
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Is, Default)]
32-
pub enum LintKind {
33-
Spelling,
34-
Capitalization,
35-
Style,
36-
Formatting,
37-
Repetition,
38-
Enhancement,
39-
Readability,
40-
WordChoice,
41-
#[default]
42-
Miscellaneous,
43-
}
44-
45-
impl Display for LintKind {
46-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47-
let s = match self {
48-
LintKind::Spelling => "Spelling",
49-
LintKind::Capitalization => "Capitalization",
50-
LintKind::Formatting => "Formatting",
51-
LintKind::Repetition => "Repetition",
52-
LintKind::Readability => "Readability",
53-
LintKind::Miscellaneous => "Miscellaneous",
54-
LintKind::Enhancement => "Enhancement",
55-
LintKind::WordChoice => "Word Choice",
56-
LintKind::Style => "Style",
57-
};
58-
59-
write!(f, "{}", s)
60-
}
61-
}
62-
63-
#[derive(Debug, Clone, Serialize, Deserialize, Is, PartialEq, Eq)]
64-
pub enum Suggestion {
65-
ReplaceWith(Vec<char>),
66-
/// Insert the provided characters _after_ the offending text.
67-
InsertAfter(Vec<char>),
68-
Remove,
69-
}
70-
71-
impl Suggestion {
72-
/// Variant of [`Self::replace_with_match_case`] that accepts a static string.
73-
pub fn replace_with_match_case_str(value: &'static str, template: &[char]) -> Self {
74-
Self::replace_with_match_case(value.chars().collect(), template)
75-
}
76-
77-
/// Construct an instance of [`Self::ReplaceWith`], but make the content match the case of the
78-
/// provided template.
79-
///
80-
/// For example, if we want to replace "You're" with "You are", we can provide "you are" and
81-
/// "You're".
82-
pub fn replace_with_match_case(mut value: Vec<char>, template: &[char]) -> Self {
83-
for (v, t) in value.iter_mut().zip(template.iter()) {
84-
if v.is_ascii_uppercase() != t.is_ascii_uppercase() {
85-
if t.is_uppercase() {
86-
*v = v.to_ascii_uppercase();
87-
} else {
88-
*v = v.to_ascii_lowercase();
89-
}
90-
}
91-
}
92-
93-
Self::ReplaceWith(value)
94-
}
95-
96-
/// Apply a suggestion to a given text.
97-
pub fn apply(&self, span: Span, source: &mut Vec<char>) {
98-
match self {
99-
Self::ReplaceWith(chars) => {
100-
// Avoid allocation if possible
101-
if chars.len() == span.len() {
102-
for (index, c) in chars.iter().enumerate() {
103-
source[index + span.start] = *c
104-
}
105-
} else {
106-
let popped = source.split_off(span.start);
107-
108-
source.extend(chars);
109-
source.extend(popped.into_iter().skip(span.len()));
110-
}
111-
}
112-
Self::Remove => {
113-
for i in span.end..source.len() {
114-
source[i - span.len()] = source[i];
115-
}
116-
117-
source.truncate(source.len() - span.len());
118-
}
119-
Self::InsertAfter(chars) => {
120-
let popped = source.split_off(span.end);
121-
source.extend(chars);
122-
source.extend(popped);
123-
}
124-
}
125-
}
126-
}
127-
128-
impl Display for Suggestion {
129-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130-
match self {
131-
Suggestion::ReplaceWith(with) => {
132-
write!(f, "Replace with: “{}”", with.iter().collect::<String>())
133-
}
134-
Suggestion::InsertAfter(with) => {
135-
write!(f, "Insert “{}”", with.iter().collect::<String>())
136-
}
137-
Suggestion::Remove => write!(f, "Remove error"),
138-
}
139-
}
140-
}
141-
142-
#[cfg(test)]
143-
mod tests {
144-
use crate::Span;
145-
146-
use super::Suggestion;
147-
148-
#[test]
149-
fn insert_comma_after() {
150-
let source = "This is a test";
151-
let mut source_chars = source.chars().collect();
152-
let sug = Suggestion::InsertAfter(vec![',']);
153-
sug.apply(Span::new(0, 4), &mut source_chars);
154-
155-
assert_eq!(source_chars, "This, is a test".chars().collect::<Vec<_>>());
156-
}
157-
158-
#[test]
159-
fn suggestion_your_match_case() {
160-
let template: Vec<_> = "You're".chars().collect();
161-
let value: Vec<_> = "you are".chars().collect();
162-
163-
let correct = "You are".chars().collect();
164-
165-
assert_eq!(
166-
Suggestion::replace_with_match_case(value, &template),
167-
Suggestion::ReplaceWith(correct)
168-
)
169-
}
170-
}

harper-core/src/linting/lint_kind.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use std::fmt::Display;
2+
3+
use is_macro::Is;
4+
use serde::{Deserialize, Serialize};
5+
6+
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Is, Default)]
7+
pub enum LintKind {
8+
Spelling,
9+
Capitalization,
10+
Style,
11+
Formatting,
12+
Repetition,
13+
Enhancement,
14+
Readability,
15+
WordChoice,
16+
#[default]
17+
Miscellaneous,
18+
}
19+
20+
impl Display for LintKind {
21+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22+
let s = match self {
23+
LintKind::Spelling => "Spelling",
24+
LintKind::Capitalization => "Capitalization",
25+
LintKind::Formatting => "Formatting",
26+
LintKind::Repetition => "Repetition",
27+
LintKind::Readability => "Readability",
28+
LintKind::Miscellaneous => "Miscellaneous",
29+
LintKind::Enhancement => "Enhancement",
30+
LintKind::WordChoice => "Word Choice",
31+
LintKind::Style => "Style",
32+
};
33+
34+
write!(f, "{}", s)
35+
}
36+
}

harper-core/src/linting/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod lets_confusion;
1111
mod linking_verbs;
1212
mod lint;
1313
mod lint_group;
14+
mod lint_kind;
1415
mod long_sentences;
1516
mod matcher;
1617
mod merge_linters;
@@ -28,6 +29,7 @@ mod somewhat_something;
2829
mod spaces;
2930
mod spell_check;
3031
mod spelled_numbers;
32+
mod suggestion;
3133
mod terminating_conjunctions;
3234
mod that_which;
3335
mod unclosed_quotes;
@@ -44,8 +46,9 @@ pub use dot_initialisms::DotInitialisms;
4446
pub use ellipsis_length::EllipsisLength;
4547
pub use lets_confusion::LetsConfusion;
4648
pub use linking_verbs::LinkingVerbs;
47-
pub use lint::{Lint, LintKind, Suggestion};
49+
pub use lint::Lint;
4850
pub use lint_group::{LintGroup, LintGroupConfig};
51+
pub use lint_kind::LintKind;
4952
pub use long_sentences::LongSentences;
5053
pub use matcher::Matcher;
5154
pub use merge_words::MergeWords;
@@ -65,6 +68,7 @@ pub use somewhat_something::SomewhatSomething;
6568
pub use spaces::Spaces;
6669
pub use spell_check::SpellCheck;
6770
pub use spelled_numbers::SpelledNumbers;
71+
pub use suggestion::Suggestion;
6872
pub use terminating_conjunctions::TerminatingConjunctions;
6973
pub use that_which::ThatWhich;
7074
pub use unclosed_quotes::UnclosedQuotes;

harper-core/src/linting/sentence_capitalization.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use itertools::Itertools;
22

3-
use super::lint::Suggestion;
3+
use super::Suggestion;
44
use super::{Lint, LintKind, Linter};
55
use crate::document::Document;
66
use crate::{Token, TokenKind, TokenStringExt};

harper-core/src/linting/spell_check.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use hashbrown::HashMap;
22
use smallvec::ToSmallVec;
33

4-
use super::lint::Suggestion;
5-
use super::{Lint, LintKind, Linter};
4+
use super::{Lint, LintKind, Linter, Suggestion};
65
use crate::document::Document;
76
use crate::spell::suggest_correct_spelling;
87
use crate::{CharString, Dictionary, TokenStringExt};

harper-core/src/linting/suggestion.rs

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use std::fmt::Display;
2+
3+
use is_macro::Is;
4+
use serde::{Deserialize, Serialize};
5+
6+
use crate::Span;
7+
8+
#[derive(Debug, Clone, Serialize, Deserialize, Is, PartialEq, Eq)]
9+
pub enum Suggestion {
10+
ReplaceWith(Vec<char>),
11+
/// Insert the provided characters _after_ the offending text.
12+
InsertAfter(Vec<char>),
13+
Remove,
14+
}
15+
16+
impl Suggestion {
17+
/// Variant of [`Self::replace_with_match_case`] that accepts a static string.
18+
pub fn replace_with_match_case_str(value: &'static str, template: &[char]) -> Self {
19+
Self::replace_with_match_case(value.chars().collect(), template)
20+
}
21+
22+
/// Construct an instance of [`Self::ReplaceWith`], but make the content match the case of the
23+
/// provided template.
24+
///
25+
/// For example, if we want to replace "You're" with "You are", we can provide "you are" and
26+
/// "You're".
27+
pub fn replace_with_match_case(mut value: Vec<char>, template: &[char]) -> Self {
28+
for (v, t) in value.iter_mut().zip(template.iter()) {
29+
if v.is_ascii_uppercase() != t.is_ascii_uppercase() {
30+
if t.is_uppercase() {
31+
*v = v.to_ascii_uppercase();
32+
} else {
33+
*v = v.to_ascii_lowercase();
34+
}
35+
}
36+
}
37+
38+
Self::ReplaceWith(value)
39+
}
40+
41+
/// Apply a suggestion to a given text.
42+
pub fn apply(&self, span: Span, source: &mut Vec<char>) {
43+
match self {
44+
Self::ReplaceWith(chars) => {
45+
// Avoid allocation if possible
46+
if chars.len() == span.len() {
47+
for (index, c) in chars.iter().enumerate() {
48+
source[index + span.start] = *c
49+
}
50+
} else {
51+
let popped = source.split_off(span.start);
52+
53+
source.extend(chars);
54+
source.extend(popped.into_iter().skip(span.len()));
55+
}
56+
}
57+
Self::Remove => {
58+
for i in span.end..source.len() {
59+
source[i - span.len()] = source[i];
60+
}
61+
62+
source.truncate(source.len() - span.len());
63+
}
64+
Self::InsertAfter(chars) => {
65+
let popped = source.split_off(span.end);
66+
source.extend(chars);
67+
source.extend(popped);
68+
}
69+
}
70+
}
71+
}
72+
73+
impl Display for Suggestion {
74+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75+
match self {
76+
Suggestion::ReplaceWith(with) => {
77+
write!(f, "Replace with: “{}”", with.iter().collect::<String>())
78+
}
79+
Suggestion::InsertAfter(with) => {
80+
write!(f, "Insert “{}”", with.iter().collect::<String>())
81+
}
82+
Suggestion::Remove => write!(f, "Remove error"),
83+
}
84+
}
85+
}
86+
87+
#[cfg(test)]
88+
mod tests {
89+
use crate::Span;
90+
91+
use super::Suggestion;
92+
93+
#[test]
94+
fn insert_comma_after() {
95+
let source = "This is a test";
96+
let mut source_chars = source.chars().collect();
97+
let sug = Suggestion::InsertAfter(vec![',']);
98+
sug.apply(Span::new(0, 4), &mut source_chars);
99+
100+
assert_eq!(source_chars, "This, is a test".chars().collect::<Vec<_>>());
101+
}
102+
103+
#[test]
104+
fn suggestion_your_match_case() {
105+
let template: Vec<_> = "You're".chars().collect();
106+
let value: Vec<_> = "you are".chars().collect();
107+
108+
let correct = "You are".chars().collect();
109+
110+
assert_eq!(
111+
Suggestion::replace_with_match_case(value, &template),
112+
Suggestion::ReplaceWith(correct)
113+
)
114+
}
115+
}

0 commit comments

Comments
 (0)