Skip to main content

mdwright_lint/stdlib/
inconsistent_list_marker.rs

1//! Mixed `-` / `*` / `+` markers within one bullet list.
2//!
3//! Pick one and stick to it. Mixed markers look like a stale merge
4//! and confuse renderers that re-emit the list with a normalised
5//! marker. Ordered lists are not flagged — their markers are digits
6//! and have semantic meaning.
7
8use crate::diagnostic::Diagnostic;
9use crate::rule::LintRule;
10use mdwright_document::Document;
11
12pub struct InconsistentListMarker;
13
14impl LintRule for InconsistentListMarker {
15    fn name(&self) -> &str {
16        "inconsistent-list-marker"
17    }
18
19    fn description(&self) -> &str {
20        "Mixed `-` / `*` / `+` markers in one bullet list."
21    }
22
23    fn explain(&self) -> &str {
24        include_str!("explain/inconsistent_list_marker.md")
25    }
26
27    fn check(&self, doc: &Document, out: &mut Vec<Diagnostic>) {
28        for group in doc.list_groups() {
29            if group.ordered || group.items.is_empty() {
30                continue;
31            }
32            let Some(first) = group.items.first() else {
33                continue;
34            };
35            let expected = first.marker_byte;
36            for item in group.items.iter().skip(1) {
37                if item.marker_byte != expected {
38                    let actual = item.marker_byte as char;
39                    let expected_c = expected as char;
40                    let message = format!(
41                        "list marker `{actual}` does not match first item's `{expected_c}` \
42                         — keep one marker per list"
43                    );
44                    let start = item.raw_range.start;
45                    let local = 0..1;
46                    if let Some(d) = Diagnostic::at(doc, start, local, message, None) {
47                        out.push(d);
48                    }
49                }
50            }
51        }
52    }
53}