harper_core/linting/
quote_spacing.rs

1use crate::expr::{ExprExt, SequenceExpr};
2use crate::linting::LintKind;
3use crate::{Document, TokenStringExt};
4
5use super::{Lint, Linter};
6
7pub struct QuoteSpacing {
8    expr: SequenceExpr,
9}
10
11impl QuoteSpacing {
12    pub fn new() -> Self {
13        Self {
14            expr: SequenceExpr::default()
15                .then_any_word()
16                .then_quote()
17                .then_any_word(),
18        }
19    }
20}
21
22impl Default for QuoteSpacing {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl Linter for QuoteSpacing {
29    fn lint(&mut self, document: &Document) -> Vec<Lint> {
30        let mut lints = Vec::new();
31
32        for m in self.expr.iter_matches_in_doc(document) {
33            let matched_tokens = m.get_content(document.get_tokens());
34
35            let Some(span) = matched_tokens.span() else {
36                continue;
37            };
38
39            lints.push(Lint {
40                span,
41                lint_kind: LintKind::Formatting,
42                suggestions: vec![],
43                message: "A quote must be preceded or succeeded by a space.".to_owned(),
44                priority: 31,
45            })
46        }
47
48        lints
49    }
50
51    fn description(&self) -> &str {
52        "Checks that quotation marks are preceded or succeeded by whitespace."
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::QuoteSpacing;
59    use crate::linting::tests::{assert_lint_count, assert_no_lints};
60
61    #[test]
62    fn flags_missing_space_before_quote() {
63        assert_lint_count("He said\"hello\" to me.", QuoteSpacing::default(), 1);
64    }
65
66    #[test]
67    fn flags_missing_space_after_quote() {
68        assert_lint_count(
69            "She whispered \"hurry\"and left.",
70            QuoteSpacing::default(),
71            1,
72        );
73    }
74
75    #[test]
76    fn allows_quotes_with_spacing() {
77        assert_no_lints("They shouted \"charge\" together.", QuoteSpacing::default());
78    }
79
80    #[test]
81    fn allows_quotes_at_end_of_sentence() {
82        assert_no_lints("They shouted \"charge.\"", QuoteSpacing::default());
83    }
84}