@@ -6,16 +6,19 @@ use syntax::{
6
6
ast:: { self , AstNode , AstToken , VisibilityOwner } ,
7
7
Direction , NodeOrToken , SourceFile ,
8
8
SyntaxKind :: { self , * } ,
9
- SyntaxNode , TextRange ,
9
+ SyntaxNode , TextRange , TextSize ,
10
10
} ;
11
11
12
+ use lazy_static:: lazy_static;
13
+
12
14
#[ derive( Debug , PartialEq , Eq ) ]
13
15
pub enum FoldKind {
14
16
Comment ,
15
17
Imports ,
16
18
Mods ,
17
19
Block ,
18
20
ArgList ,
21
+ Region ,
19
22
}
20
23
21
24
#[ derive( Debug ) ]
@@ -29,6 +32,8 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
29
32
let mut visited_comments = FxHashSet :: default ( ) ;
30
33
let mut visited_imports = FxHashSet :: default ( ) ;
31
34
let mut visited_mods = FxHashSet :: default ( ) ;
35
+ // regions can be nested, here is a LIFO buffer
36
+ let mut regions_starts: Vec < TextSize > = vec ! [ ] ;
32
37
33
38
for element in file. syntax ( ) . descendants_with_tokens ( ) {
34
39
// Fold items that span multiple lines
@@ -48,10 +53,32 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
48
53
// Fold groups of comments
49
54
if let Some ( comment) = ast:: Comment :: cast ( token) {
50
55
if !visited_comments. contains ( & comment) {
51
- if let Some ( range) =
52
- contiguous_range_for_comment ( comment, & mut visited_comments)
53
- {
54
- res. push ( Fold { range, kind : FoldKind :: Comment } )
56
+ // regions are not really comments
57
+ use regex:: Regex ;
58
+ lazy_static ! {
59
+ static ref RE_START : Regex =
60
+ Regex :: new( r"^\s*//\s*#?region\b" ) . unwrap( ) ;
61
+ static ref RE_END : Regex =
62
+ Regex :: new( r"^\s*//\s*#?endregion\b" ) . unwrap( ) ;
63
+ }
64
+ if RE_START . is_match ( comment. text ( ) ) {
65
+ regions_starts. push ( comment. syntax ( ) . text_range ( ) . start ( ) ) ;
66
+ } else if RE_END . is_match ( comment. text ( ) ) {
67
+ if !regions_starts. is_empty ( ) {
68
+ res. push ( Fold {
69
+ range : TextRange :: new (
70
+ regions_starts. pop ( ) . unwrap ( ) ,
71
+ comment. syntax ( ) . text_range ( ) . end ( ) ,
72
+ ) ,
73
+ kind : FoldKind :: Region ,
74
+ } )
75
+ }
76
+ } else {
77
+ if let Some ( range) =
78
+ contiguous_range_for_comment ( comment, & mut visited_comments)
79
+ {
80
+ res. push ( Fold { range, kind : FoldKind :: Comment } )
81
+ }
55
82
}
56
83
}
57
84
}
@@ -175,9 +202,21 @@ fn contiguous_range_for_comment(
175
202
}
176
203
if let Some ( c) = ast:: Comment :: cast ( token) {
177
204
if c. kind ( ) == group_kind {
178
- visited. insert ( c. clone ( ) ) ;
179
- last = c;
180
- continue ;
205
+ // regions are not really comments
206
+ use regex:: Regex ;
207
+ lazy_static ! {
208
+ static ref RE_START : Regex =
209
+ Regex :: new( r"^\s*//\s*#?region\b" ) . unwrap( ) ;
210
+ static ref RE_END : Regex =
211
+ Regex :: new( r"^\s*//\s*#?endregion\b" ) . unwrap( ) ;
212
+ }
213
+ if RE_START . is_match ( c. text ( ) ) || RE_END . is_match ( c. text ( ) ) {
214
+ break ;
215
+ } else {
216
+ visited. insert ( c. clone ( ) ) ;
217
+ last = c;
218
+ continue ;
219
+ }
181
220
}
182
221
}
183
222
// The comment group ends because either:
@@ -224,6 +263,7 @@ mod tests {
224
263
FoldKind :: Mods => "mods" ,
225
264
FoldKind :: Block => "block" ,
226
265
FoldKind :: ArgList => "arglist" ,
266
+ FoldKind :: Region => "region" ,
227
267
} ;
228
268
assert_eq ! ( kind, & attr. unwrap( ) ) ;
229
269
}
@@ -418,4 +458,24 @@ fn foo<fold arglist>(
418
458
"# ,
419
459
)
420
460
}
461
+
462
+ #[ test]
463
+ fn fold_region ( ) {
464
+ log_init_for_test_debug ( ) ;
465
+ // only error level log is printed on the terminal
466
+ log:: error!( "test fold_region" ) ;
467
+ check (
468
+ r#"
469
+ // 1. some normal comment
470
+ <fold region>// region: test
471
+ // 2. some normal comment
472
+ calling_function(x,y);
473
+ // endregion: test</fold>
474
+ "# ,
475
+ )
476
+ }
477
+
478
+ fn log_init_for_test_debug ( ) {
479
+ let _ = env_logger:: builder ( ) . is_test ( true ) . try_init ( ) ;
480
+ }
421
481
}
0 commit comments