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_idx in sentence.iter_space_indices() {
14                if space_idx == 0 {
15                    continue;
16                }
17
18                let space = &sentence[space_idx];
19
20                let TokenKind::Space(count) = space.kind else {
21                    panic!("The space iterator should only return spaces.")
22                };
23
24                if count > 1 {
25                    output.push(Lint {
26                        span: space.span,
27                        lint_kind: LintKind::Formatting,
28                        suggestions: vec![Suggestion::ReplaceWith(vec![' '])],
29                        message: format!(
30                            "There are {count} spaces where there should be only one."
31                        ),
32                        priority: 15,
33                    })
34                }
35            }
36
37            if matches!(
38                sentence,
39                [
40                    ..,
41                    Token {
42                        kind: TokenKind::Word(_),
43                        ..
44                    },
45                    Token {
46                        kind: TokenKind::Space(_),
47                        ..
48                    },
49                    Token {
50                        kind: TokenKind::Punctuation(_),
51                        ..
52                    }
53                ]
54            ) {
55                output.push(Lint {
56                    span: sentence[sentence.len() - 2..sentence.len() - 1]
57                        .span()
58                        .unwrap(),
59                    lint_kind: LintKind::Formatting,
60                    suggestions: vec![Suggestion::Remove],
61                    message: "Unnecessary space at the end of the sentence.".to_string(),
62                    priority: 63,
63                })
64            }
65        }
66
67        output
68    }
69
70    fn description(&self) -> &'static str {
71        "Words should be separated by at most one space."
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::Spaces;
78    use crate::linting::tests::{assert_lint_count, assert_no_lints};
79
80    #[test]
81    fn detects_space_before_period() {
82        let source = "There is a space at the end of this sentence .";
83
84        assert_lint_count(source, Spaces, 1)
85    }
86
87    #[test]
88    fn allows_period_without_space() {
89        let source = "There isn't a space at the end of this sentence.";
90
91        assert_lint_count(source, Spaces, 0)
92    }
93
94    #[test]
95    fn ignores_french_spacing() {
96        assert_no_lints(
97            "This is a short sentence.  This is another short sentence.",
98            Spaces,
99        );
100    }
101}