6
6
//!
7
7
//! Hopefully, one day a reliable file watching/walking crate appears on
8
8
//! crates.io, and we can reduce this to trivial glue code.
9
- mod include;
10
-
11
- use std:: convert:: { TryFrom , TryInto } ;
9
+ use std:: convert:: TryFrom ;
12
10
13
11
use crossbeam_channel:: { never, select, unbounded, Receiver , Sender } ;
14
12
use notify:: { RecommendedWatcher , RecursiveMode , Watcher } ;
15
13
use paths:: { AbsPath , AbsPathBuf } ;
16
14
use vfs:: loader;
17
15
use walkdir:: WalkDir ;
18
16
19
- use crate :: include:: Include ;
20
-
21
17
#[ derive( Debug ) ]
22
18
pub struct NotifyHandle {
23
19
// Relative order of fields below is significant.
@@ -53,7 +49,7 @@ type NotifyEvent = notify::Result<notify::Event>;
53
49
54
50
struct NotifyActor {
55
51
sender : loader:: Sender ,
56
- config : Vec < ( AbsPathBuf , Include , bool ) > ,
52
+ watched_entries : Vec < loader :: Entry > ,
57
53
// Drop order is significant.
58
54
watcher : Option < ( RecommendedWatcher , Receiver < NotifyEvent > ) > ,
59
55
}
@@ -66,7 +62,7 @@ enum Event {
66
62
67
63
impl NotifyActor {
68
64
fn new ( sender : loader:: Sender ) -> NotifyActor {
69
- NotifyActor { sender, config : Vec :: new ( ) , watcher : None }
65
+ NotifyActor { sender, watched_entries : Vec :: new ( ) , watcher : None }
70
66
}
71
67
fn next_event ( & self , receiver : & Receiver < Message > ) -> Option < Event > {
72
68
let watcher_receiver = self . watcher . as_ref ( ) . map ( |( _, receiver) | receiver) ;
@@ -93,15 +89,17 @@ impl NotifyActor {
93
89
let n_total = config. load . len ( ) ;
94
90
self . send ( loader:: Message :: Progress { n_total, n_done : 0 } ) ;
95
91
96
- self . config . clear ( ) ;
92
+ self . watched_entries . clear ( ) ;
97
93
98
94
for ( i, entry) in config. load . into_iter ( ) . enumerate ( ) {
99
95
let watch = config. watch . contains ( & i) ;
96
+ if watch {
97
+ self . watched_entries . push ( entry. clone ( ) )
98
+ }
100
99
let files = self . load_entry ( entry, watch) ;
101
100
self . send ( loader:: Message :: Loaded { files } ) ;
102
101
self . send ( loader:: Message :: Progress { n_total, n_done : i + 1 } ) ;
103
102
}
104
- self . config . sort_by ( |x, y| x. 0 . cmp ( & y. 0 ) ) ;
105
103
}
106
104
Message :: Invalidate ( path) => {
107
105
let contents = read ( path. as_path ( ) ) ;
@@ -116,34 +114,27 @@ impl NotifyActor {
116
114
. into_iter ( )
117
115
. map ( |path| AbsPathBuf :: try_from ( path) . unwrap ( ) )
118
116
. filter_map ( |path| {
119
- let is_dir = path. is_dir ( ) ;
120
- let is_file = path. is_file ( ) ;
121
-
122
- let config_idx =
123
- match self . config . binary_search_by ( |it| it. 0 . cmp ( & path) ) {
124
- Ok ( it) => it,
125
- Err ( it) => it. saturating_sub ( 1 ) ,
126
- } ;
127
- let include = self . config . get ( config_idx) . and_then ( |it| {
128
- let rel_path = path. strip_prefix ( & it. 0 ) ?;
129
- Some ( ( rel_path, & it. 1 ) )
130
- } ) ;
131
-
132
- if let Some ( ( rel_path, include) ) = include {
133
- if is_dir && include. exclude_dir ( & rel_path)
134
- || is_file && !include. include_file ( & rel_path)
135
- {
136
- return None ;
137
- }
117
+ if path. is_dir ( )
118
+ && self
119
+ . watched_entries
120
+ . iter ( )
121
+ . any ( |entry| entry. contains_dir ( & path) )
122
+ {
123
+ self . watch ( path) ;
124
+ return None ;
138
125
}
139
126
140
- if is_dir {
141
- self . watch ( path) ;
127
+ if !path. is_file ( ) {
142
128
return None ;
143
129
}
144
- if !is_file {
130
+ if !self
131
+ . watched_entries
132
+ . iter ( )
133
+ . any ( |entry| entry. contains_file ( & path) )
134
+ {
145
135
return None ;
146
136
}
137
+
147
138
let contents = read ( & path) ;
148
139
Some ( ( path, contents) )
149
140
} )
@@ -170,43 +161,42 @@ impl NotifyActor {
170
161
( file, contents)
171
162
} )
172
163
. collect :: < Vec < _ > > ( ) ,
173
- loader:: Entry :: Directory { path, include } => {
174
- let include = Include :: new ( include) ;
175
- self . config . push ( ( path. clone ( ) , include. clone ( ) , watch) ) ;
176
-
177
- let files = WalkDir :: new ( & path)
178
- . into_iter ( )
179
- . filter_entry ( |entry| {
180
- let abs_path: & AbsPath = entry. path ( ) . try_into ( ) . unwrap ( ) ;
181
- match abs_path. strip_prefix ( & path) {
182
- Some ( rel_path) => {
183
- !( entry. file_type ( ) . is_dir ( ) && include. exclude_dir ( rel_path) )
184
- }
185
- None => false ,
164
+ loader:: Entry :: Directories ( dirs) => {
165
+ let mut res = Vec :: new ( ) ;
166
+
167
+ for root in dirs. include . iter ( ) {
168
+ let walkdir = WalkDir :: new ( root) . into_iter ( ) . filter_entry ( |entry| {
169
+ if !entry. file_type ( ) . is_dir ( ) {
170
+ return true ;
186
171
}
187
- } )
188
- . filter_map ( |entry| entry. ok ( ) )
189
- . filter_map ( |entry| {
172
+ let path = AbsPath :: assert ( entry. path ( ) ) ;
173
+ root == path
174
+ || dirs. exclude . iter ( ) . chain ( & dirs. include ) . all ( |it| it != path)
175
+ } ) ;
176
+
177
+ let files = walkdir. filter_map ( |it| it. ok ( ) ) . filter_map ( |entry| {
190
178
let is_dir = entry. file_type ( ) . is_dir ( ) ;
191
179
let is_file = entry. file_type ( ) . is_file ( ) ;
192
- let abs_path = AbsPathBuf :: try_from ( entry. into_path ( ) ) . unwrap ( ) ;
180
+ let abs_path = AbsPathBuf :: assert ( entry. into_path ( ) ) ;
193
181
if is_dir && watch {
194
182
self . watch ( abs_path. clone ( ) ) ;
195
183
}
196
- let rel_path = abs_path. strip_prefix ( & path) ?;
197
- if is_file && include. include_file ( & rel_path) {
198
- Some ( abs_path)
199
- } else {
200
- None
184
+ if !is_file {
185
+ return None ;
186
+ }
187
+ let ext = abs_path. extension ( ) . unwrap_or_default ( ) ;
188
+ if dirs. extensions . iter ( ) . all ( |it| it. as_str ( ) != ext) {
189
+ return None ;
201
190
}
191
+ Some ( abs_path)
202
192
} ) ;
203
193
204
- files
205
- . map ( |file| {
194
+ res. extend ( files. map ( |file| {
206
195
let contents = read ( file. as_path ( ) ) ;
207
196
( file, contents)
208
- } )
209
- . collect ( )
197
+ } ) ) ;
198
+ }
199
+ res
210
200
}
211
201
}
212
202
}
0 commit comments