harper_core/linting/
map_phrase_linter.rs1use super::{Lint, LintKind, PatternLinter};
2use crate::linting::Suggestion;
3use crate::patterns::{EitherPattern, ExactPhrase, Pattern, SimilarToPhrase};
4use crate::{Token, TokenStringExt};
5
6pub struct MapPhraseLinter {
7 description: String,
8 pattern: Box<dyn Pattern>,
9 correct_forms: Vec<String>,
10 message: String,
11}
12
13impl MapPhraseLinter {
14 pub fn new(
15 pattern: Box<dyn Pattern>,
16 correct_forms: impl IntoIterator<Item = impl ToString>,
17 message: impl ToString,
18 description: impl ToString,
19 ) -> Self {
20 Self {
21 description: description.to_string(),
22 pattern,
23 correct_forms: correct_forms.into_iter().map(|f| f.to_string()).collect(),
24 message: message.to_string(),
25 }
26 }
27
28 pub fn new_similar_to_phrase(phrase: &'static str, detectable_distance: u8) -> Self {
29 Self::new(
30 Box::new(SimilarToPhrase::from_phrase(phrase, detectable_distance)),
31 [phrase],
32 format!("Did you mean the phrase `{phrase}`?"),
33 format!("Looks for slight improper modifications to the phrase `{phrase}`."),
34 )
35 }
36
37 pub fn new_exact_phrases(
38 phrase: impl IntoIterator<Item = impl AsRef<str>>,
39 correct_forms: impl IntoIterator<Item = impl ToString>,
40 message: impl ToString,
41 description: impl ToString,
42 ) -> Self {
43 let patterns = EitherPattern::new(
44 phrase
45 .into_iter()
46 .map(|p| {
47 let pattern: Box<dyn Pattern> = Box::new(ExactPhrase::from_phrase(p.as_ref()));
48 pattern
49 })
50 .collect(),
51 );
52
53 Self::new(Box::new(patterns), correct_forms, message, description)
54 }
55
56 pub fn new_exact_phrase(
57 phrase: impl AsRef<str>,
58 correct_forms: impl IntoIterator<Item = impl ToString>,
59 message: impl ToString,
60 description: impl ToString,
61 ) -> Self {
62 Self::new(
63 Box::new(ExactPhrase::from_phrase(phrase.as_ref())),
64 correct_forms,
65 message,
66 description,
67 )
68 }
69
70 pub fn new_closed_compound(phrase: impl AsRef<str>, correct_form: impl ToString) -> Self {
71 let message = format!(
72 "Did you mean the closed compound `{}`?",
73 correct_form.to_string()
74 );
75
76 let description = format!(
77 "Looks for incorrect spacing inside the closed compound `{}`.",
78 correct_form.to_string()
79 );
80
81 Self::new_exact_phrase(phrase, [correct_form], message, description)
82 }
83}
84
85impl PatternLinter for MapPhraseLinter {
86 fn pattern(&self) -> &dyn Pattern {
87 self.pattern.as_ref()
88 }
89
90 fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Option<Lint> {
91 let span = matched_tokens.span()?;
92 let matched_text = span.get_content(source);
93
94 Some(Lint {
95 span,
96 lint_kind: LintKind::Miscellaneous,
97 suggestions: self
98 .correct_forms
99 .iter()
100 .map(|correct_form| {
101 Suggestion::replace_with_match_case(
102 correct_form.chars().collect(),
103 matched_text,
104 )
105 })
106 .collect(),
107 message: self.message.to_string(),
108 priority: 31,
109 })
110 }
111
112 fn description(&self) -> &str {
113 self.description.as_str()
114 }
115}