@@ -23,6 +23,16 @@ pub enum InlineConfigItem {
2323 DisableStart ,
2424 /// Disables formatting for any code that precedes this and after the previous "disable-start"
2525 DisableEnd ,
26+ /// Ignores the next code item for linting rules
27+ IgnoreNextItem ,
28+ /// Ignores the current line for linting rules
29+ IgnoreLine ,
30+ /// Ignores the next line for linting rules
31+ IgnoreNextLine ,
32+ /// Ignores linting rules for any code that follows this and before the next "ignore-end"
33+ IgnoreStart ,
34+ /// Ignores linting rules for any code that precedes this and after the previous "ignore-start"
35+ IgnoreEnd ,
2636}
2737
2838impl FromStr for InlineConfigItem {
@@ -34,6 +44,11 @@ impl FromStr for InlineConfigItem {
3444 "disable-next-line" => InlineConfigItem :: DisableNextLine ,
3545 "disable-start" => InlineConfigItem :: DisableStart ,
3646 "disable-end" => InlineConfigItem :: DisableEnd ,
47+ "ignore-next-item" => InlineConfigItem :: IgnoreNextItem ,
48+ "ignore-line" => InlineConfigItem :: IgnoreLine ,
49+ "ignore-next-line" => InlineConfigItem :: IgnoreNextLine ,
50+ "ignore-start" => InlineConfigItem :: IgnoreStart ,
51+ "ignore-end" => InlineConfigItem :: IgnoreEnd ,
3752 s => return Err ( InvalidInlineConfigItem ( s. into ( ) ) ) ,
3853 } )
3954 }
@@ -64,23 +79,45 @@ impl DisabledRange {
6479 }
6580}
6681
67- /// An inline config. Keeps track of disabled ranges.
68- ///
82+ /// An ignored formatting range. `loose` designates that the range includes any loc which
83+ /// may start in between start and end, whereas the strict version requires that
84+ /// `range.start >= loc.start <=> loc.end <= range.end`
85+ #[ derive( Debug ) ]
86+ struct IgnoredRange {
87+ start : usize ,
88+ end : usize ,
89+ loose : bool ,
90+ }
91+
92+ impl IgnoredRange {
93+ fn includes ( & self , loc : Loc ) -> bool {
94+ loc. start ( ) >= self . start && ( if self . loose { loc. start ( ) } else { loc. end ( ) } <= self . end )
95+ }
96+ }
97+
6998/// This is a list of Inline Config items for locations in a source file. This is acquired by
7099/// parsing the comments for `scopelint:` items. See [`Comments::parse_inline_config_items`] for
71100/// details.
72101#[ derive( Default , Debug ) ]
73102pub struct InlineConfig {
74103 disabled_ranges : Vec < DisabledRange > ,
104+ ignored_ranges : Vec < IgnoredRange > ,
75105}
76106
77107impl InlineConfig {
78108 /// Build a new inline config with an iterator of inline config items and their locations in a
79109 /// source file
80110 pub fn new ( items : impl IntoIterator < Item = ( Loc , InlineConfigItem ) > , src : & str ) -> Self {
111+ // Disable ranges (for formatting)
81112 let mut disabled_ranges = vec ! [ ] ;
82113 let mut disabled_range_start = None ;
83114 let mut disabled_depth = 0usize ;
115+
116+ // Ignore ranges (for linting)
117+ let mut ignored_ranges = vec ! [ ] ;
118+ let mut ignored_range_start = None ;
119+ let mut ignored_depth = 0usize ;
120+
84121 for ( loc, item) in items. into_iter ( ) . sorted_by_key ( |( loc, _) | loc. start ( ) ) {
85122 match item {
86123 InlineConfigItem :: DisableNextItem => {
@@ -145,16 +182,99 @@ impl InlineConfig {
145182 }
146183 }
147184 }
185+ InlineConfigItem :: IgnoreNextItem => {
186+ let offset = loc. end ( ) ;
187+ let mut char_indices = src[ offset..]
188+ . comment_state_char_indices ( )
189+ . filter_map ( |( state, idx, ch) | match state {
190+ CommentState :: None => Some ( ( idx, ch) ) ,
191+ _ => None ,
192+ } )
193+ . skip_while ( |( _, ch) | ch. is_whitespace ( ) ) ;
194+ if let Some ( ( mut start, _) ) = char_indices. next ( ) {
195+ start += offset;
196+ // Find the end of the function declaration by looking for the closing brace
197+ let mut brace_count = 0 ;
198+ let mut found_function_start = false ;
199+ let mut end = src. len ( ) ;
200+
201+ for ( idx, ch) in src[ start..] . char_indices ( ) {
202+ if ch == '{' {
203+ brace_count += 1 ;
204+ found_function_start = true ;
205+ } else if ch == '}' {
206+ brace_count -= 1 ;
207+ if found_function_start && brace_count == 0 {
208+ end = start + idx + 1 ;
209+ break ;
210+ }
211+ }
212+ }
213+ ignored_ranges. push ( IgnoredRange { start, end, loose : true } ) ;
214+ }
215+ }
216+ InlineConfigItem :: IgnoreLine => {
217+ let mut prev_newline =
218+ src[ ..loc. start ( ) ] . char_indices ( ) . rev ( ) . skip_while ( |( _, ch) | * ch != '\n' ) ;
219+ let start = prev_newline. next ( ) . map ( |( idx, _) | idx) . unwrap_or_default ( ) ;
220+
221+ let end_offset = loc. end ( ) ;
222+ let mut next_newline =
223+ src[ end_offset..] . char_indices ( ) . skip_while ( |( _, ch) | * ch != '\n' ) ;
224+ let end =
225+ end_offset + next_newline. next ( ) . map ( |( idx, _) | idx) . unwrap_or_default ( ) ;
226+
227+ ignored_ranges. push ( IgnoredRange { start, end, loose : false } ) ;
228+ }
229+ InlineConfigItem :: IgnoreNextLine => {
230+ let offset = loc. end ( ) ;
231+ let mut char_indices =
232+ src[ offset..] . char_indices ( ) . skip_while ( |( _, ch) | * ch != '\n' ) . skip ( 1 ) ;
233+ if let Some ( ( mut start, _) ) = char_indices. next ( ) {
234+ start += offset;
235+ let end = char_indices
236+ . find ( |( _, ch) | * ch == '\n' )
237+ . map ( |( idx, _) | offset + idx + 1 )
238+ . unwrap_or ( src. len ( ) ) ;
239+ ignored_ranges. push ( IgnoredRange { start, end, loose : false } ) ;
240+ }
241+ }
242+ InlineConfigItem :: IgnoreStart => {
243+ if ignored_depth == 0 {
244+ ignored_range_start = Some ( loc. end ( ) ) ;
245+ }
246+ ignored_depth += 1 ;
247+ }
248+ InlineConfigItem :: IgnoreEnd => {
249+ ignored_depth = ignored_depth. saturating_sub ( 1 ) ;
250+ if ignored_depth == 0 {
251+ if let Some ( start) = ignored_range_start. take ( ) {
252+ ignored_ranges. push ( IgnoredRange {
253+ start,
254+ end : loc. start ( ) ,
255+ loose : false ,
256+ } )
257+ }
258+ }
259+ }
148260 }
149261 }
150262 if let Some ( start) = disabled_range_start. take ( ) {
151263 disabled_ranges. push ( DisabledRange { start, end : src. len ( ) , loose : false } )
152264 }
153- Self { disabled_ranges }
265+ if let Some ( start) = ignored_range_start. take ( ) {
266+ ignored_ranges. push ( IgnoredRange { start, end : src. len ( ) , loose : false } )
267+ }
268+ Self { disabled_ranges, ignored_ranges }
154269 }
155270
156271 /// Check if the location is in a disabled range
157272 pub fn is_disabled ( & self , loc : Loc ) -> bool {
158273 self . disabled_ranges . iter ( ) . any ( |range| range. includes ( loc) )
159274 }
275+
276+ /// Check if the location is in an ignored range
277+ pub fn is_ignored ( & self , loc : Loc ) -> bool {
278+ self . ignored_ranges . iter ( ) . any ( |range| range. includes ( loc) )
279+ }
160280}
0 commit comments