harper_core/linting/
spaces.rs

1use super::{Lint, LintKind, Linter, Suggestion};
2use crate::TokenStringExt;
3use crate::{Document, Token, TokenKind};
4
5#[derive(Debug, Default)]
6pub struct Spaces;
7
8impl Linter for Spaces {
9    fn lint(&mut self, document: &Document) -> Vec<Lint> {
10        let mut output = Vec::new();
11
12        for sentence in document.iter_sentences() {
13            for space in sentence.iter_spaces() {
14                let TokenKind::Space(count) = space.kind else {
15                    panic!("The space iterator should only return spaces.")
16                };
17
18                if count > 1 {
19                    output.push(Lint {
20                        span: space.span,
21                        lint_kind: LintKind::Formatting,
22                        suggestions: vec![Suggestion::ReplaceWith(vec![' '])],
23                        message: format!(
24                            "There are {count} spaces where there should be only one."
25                        ),
26                        priority: 15,
27                    })
28                }
29            }
30
31            if matches!(
32                sentence,
33                [
34                    ..,
35                    Token {
36                        kind: TokenKind::Word(_),
37                        ..
38                    },
39                    Token {
40                        kind: TokenKind::Space(_),
41                        ..
42                    },
43                    Token {
44                        kind: TokenKind::Punctuation(_),
45                        ..
46                    }
47                ]
48            ) {
49                output.push(Lint {
50                    span: sentence[sentence.len() - 2..sentence.len() - 1]
51                        .span()
52                        .unwrap(),
53                    lint_kind: LintKind::Formatting,
54                    suggestions: vec![Suggestion::Remove],
55                    message: "Unnecessary space at the end of the sentence.".to_string(),
56                    priority: 63,
57                })
58            }
59        }
60
61        output
62    }
63
64    fn description(&self) -> &'static str {
65        "Words should be separated by at most one space."
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::Spaces;
72    use crate::linting::tests::assert_lint_count;
73
74    #[test]
75    fn detects_space_before_period() {
76        let source = "There is a space at the end of this sentence .";
77
78        assert_lint_count(source, Spaces, 1)
79    }
80
81    #[test]
82    fn allows_period_without_space() {
83        let source = "There isn't a space at the end of this sentence.";
84
85        assert_lint_count(source, Spaces, 0)
86    }
87}