11//! Checks that a list of items is in alphabetical order
22//!
3- //! To use, use the following annotation in the code:
3+ //! Use the following marker in the code:
44//! ```rust
55//! // tidy-alphabetical-start
66//! fn aaa() {}
1010//! ```
1111//!
1212//! The following lines are ignored:
13+ //! - Empty lines
1314//! - Lines that are indented with more or less spaces than the first line
14- //! - Lines starting with `//`, `#[`, `)`, `]`, `}` if the comment has the same indentation as
15- //! the first line
15+ //! - Lines starting with `//`, `#` (except those starting with `#!`), `)`, `]`, `}` if the comment
16+ //! has the same indentation as the first line
17+ //! - Lines starting with a closing delimiter (`)`, `[`, `}`) are ignored.
1618//!
17- //! If a line ends with an opening bracket, the line is ignored and the next line will have
18- //! its extra indentation ignored .
19+ //! If a line ends with an opening delimiter, we effectively join the following line to it before
20+ //! checking it. E.g. `foo(\nbar)` is treated like `foo(bar)` .
1921
20- use std:: { fmt:: Display , path:: Path } ;
22+ use std:: fmt:: Display ;
23+ use std:: path:: Path ;
2124
2225use crate :: walk:: { filter_dirs, walk} ;
2326
27+ #[ cfg( test) ]
28+ mod tests;
29+
2430fn indentation ( line : & str ) -> usize {
2531 line. find ( |c| c != ' ' ) . unwrap_or ( 0 )
2632}
@@ -29,28 +35,36 @@ fn is_close_bracket(c: char) -> bool {
2935 matches ! ( c, ')' | ']' | '}' )
3036}
3137
32- // Don't let tidy check this here :D
33- const START_COMMENT : & str = concat ! ( "tidy-alphabetical" , "-start" ) ;
34- const END_COMMENT : & str = "tidy-alphabetical-end" ;
38+ const START_MARKER : & str = "tidy-alphabetical-start" ;
39+ const END_MARKER : & str = "tidy-alphabetical-end" ;
3540
3641fn check_section < ' a > (
3742 file : impl Display ,
3843 lines : impl Iterator < Item = ( usize , & ' a str ) > ,
44+ err : & mut dyn FnMut ( & str ) -> std:: io:: Result < ( ) > ,
3945 bad : & mut bool ,
4046) {
41- let content_lines = lines. take_while ( |( _, line) | !line. contains ( END_COMMENT ) ) ;
42-
4347 let mut prev_line = String :: new ( ) ;
4448 let mut first_indent = None ;
4549 let mut in_split_line = None ;
4650
47- for ( line_idx, line) in content_lines {
48- if line. contains ( START_COMMENT ) {
49- tidy_error ! (
51+ for ( idx, line) in lines {
52+ if line. is_empty ( ) {
53+ continue ;
54+ }
55+
56+ if line. contains ( START_MARKER ) {
57+ tidy_error_ext ! (
58+ err,
5059 bad,
51- "{file}:{} found `{START_COMMENT}` expecting `{END_COMMENT}`" ,
52- line_idx
53- )
60+ "{file}:{} found `{START_MARKER}` expecting `{END_MARKER}`" ,
61+ idx + 1
62+ ) ;
63+ return ;
64+ }
65+
66+ if line. contains ( END_MARKER ) {
67+ return ;
5468 }
5569
5670 let indent = first_indent. unwrap_or_else ( || {
@@ -60,6 +74,7 @@ fn check_section<'a>(
6074 } ) ;
6175
6276 let line = if let Some ( prev_split_line) = in_split_line {
77+ // Join the split lines.
6378 in_split_line = None ;
6479 format ! ( "{prev_split_line}{}" , line. trim_start( ) )
6580 } else {
@@ -73,7 +88,7 @@ fn check_section<'a>(
7388 let trimmed_line = line. trim_start_matches ( ' ' ) ;
7489
7590 if trimmed_line. starts_with ( "//" )
76- || trimmed_line. starts_with ( "#[" )
91+ || ( trimmed_line. starts_with ( "#" ) && !trimmed_line . starts_with ( "#!" ) )
7792 || trimmed_line. starts_with ( is_close_bracket)
7893 {
7994 continue ;
@@ -87,25 +102,44 @@ fn check_section<'a>(
87102 let prev_line_trimmed_lowercase = prev_line. trim_start_matches ( ' ' ) . to_lowercase ( ) ;
88103
89104 if trimmed_line. to_lowercase ( ) < prev_line_trimmed_lowercase {
90- tidy_error ! ( bad, "{file}:{}: line not in alphabetical order" , line_idx + 1 , ) ;
105+ tidy_error_ext ! ( err , bad, "{file}:{}: line not in alphabetical order" , idx + 1 ) ;
91106 }
92107
93108 prev_line = line;
94109 }
110+
111+ tidy_error_ext ! ( err, bad, "{file}: reached end of file expecting `{END_MARKER}`" )
95112}
96113
97- pub fn check ( path : & Path , bad : & mut bool ) {
98- walk ( path, |path, _is_dir| filter_dirs ( path) , & mut |entry, contents| {
99- let file = & entry. path ( ) . display ( ) ;
114+ fn check_lines < ' a > (
115+ file : & impl Display ,
116+ mut lines : impl Iterator < Item = ( usize , & ' a str ) > ,
117+ err : & mut dyn FnMut ( & str ) -> std:: io:: Result < ( ) > ,
118+ bad : & mut bool ,
119+ ) {
120+ while let Some ( ( idx, line) ) = lines. next ( ) {
121+ if line. contains ( END_MARKER ) {
122+ tidy_error_ext ! (
123+ err,
124+ bad,
125+ "{file}:{} found `{END_MARKER}` expecting `{START_MARKER}`" ,
126+ idx + 1
127+ )
128+ }
100129
101- let mut lines = contents. lines ( ) . enumerate ( ) . peekable ( ) ;
102- while let Some ( ( _, line) ) = lines. next ( ) {
103- if line. contains ( START_COMMENT ) {
104- check_section ( file, & mut lines, bad) ;
105- if lines. peek ( ) . is_none ( ) {
106- tidy_error ! ( bad, "{file}: reached end of file expecting `{END_COMMENT}`" )
107- }
108- }
130+ if line. contains ( START_MARKER ) {
131+ check_section ( file, & mut lines, err, bad) ;
109132 }
133+ }
134+ }
135+
136+ pub fn check ( path : & Path , bad : & mut bool ) {
137+ let skip =
138+ |path : & _ , _is_dir| filter_dirs ( path) || path. ends_with ( "tidy/src/alphabetical/tests.rs" ) ;
139+
140+ walk ( path, skip, & mut |entry, contents| {
141+ let file = & entry. path ( ) . display ( ) ;
142+ let lines = contents. lines ( ) . enumerate ( ) ;
143+ check_lines ( file, lines, & mut crate :: tidy_error, bad)
110144 } ) ;
111145}
0 commit comments