@@ -46,14 +46,15 @@ pub mod loader;
46
46
mod path_interner;
47
47
mod vfs_path;
48
48
49
- use std:: { fmt, mem} ;
49
+ use std:: { fmt, hash :: BuildHasherDefault , mem} ;
50
50
51
51
use crate :: path_interner:: PathInterner ;
52
52
53
53
pub use crate :: {
54
54
anchored_path:: { AnchoredPath , AnchoredPathBuf } ,
55
55
vfs_path:: VfsPath ,
56
56
} ;
57
+ use indexmap:: { map:: Entry , IndexMap } ;
57
58
pub use paths:: { AbsPath , AbsPathBuf } ;
58
59
59
60
use rustc_hash:: FxHasher ;
@@ -95,19 +96,12 @@ impl nohash_hasher::IsEnabled for FileId {}
95
96
pub struct Vfs {
96
97
interner : PathInterner ,
97
98
data : Vec < FileState > ,
98
- // FIXME: This should be a HashMap<FileId, ChangeFile>
99
- // right now we do a nasty deduplication in GlobalState::process_changes that would be a lot
100
- // easier to handle here on insertion.
101
- changes : Vec < ChangedFile > ,
102
- // The above FIXME would then also get rid of this probably
103
- created_this_cycle : Vec < FileId > ,
99
+ changes : IndexMap < FileId , ChangedFile , BuildHasherDefault < FxHasher > > ,
104
100
}
105
101
106
102
#[ derive( Copy , Clone , Debug , PartialEq , PartialOrd ) ]
107
103
pub enum FileState {
108
- /// The file has been created this cycle.
109
- Created ,
110
- /// The file exists.
104
+ /// The file exists with the given content hash.
111
105
Exists ( u64 ) ,
112
106
/// The file is deleted.
113
107
Deleted ,
@@ -131,12 +125,12 @@ impl ChangedFile {
131
125
/// Returns `true` if the change is [`Create`](ChangeKind::Create) or
132
126
/// [`Delete`](Change::Delete).
133
127
pub fn is_created_or_deleted ( & self ) -> bool {
134
- matches ! ( self . change, Change :: Create ( _) | Change :: Delete )
128
+ matches ! ( self . change, Change :: Create ( _, _ ) | Change :: Delete )
135
129
}
136
130
137
131
/// Returns `true` if the change is [`Create`](ChangeKind::Create).
138
132
pub fn is_created ( & self ) -> bool {
139
- matches ! ( self . change, Change :: Create ( _) )
133
+ matches ! ( self . change, Change :: Create ( _, _ ) )
140
134
}
141
135
142
136
/// Returns `true` if the change is [`Modify`](ChangeKind::Modify).
@@ -146,7 +140,7 @@ impl ChangedFile {
146
140
147
141
pub fn kind ( & self ) -> ChangeKind {
148
142
match self . change {
149
- Change :: Create ( _) => ChangeKind :: Create ,
143
+ Change :: Create ( _, _ ) => ChangeKind :: Create ,
150
144
Change :: Modify ( _, _) => ChangeKind :: Modify ,
151
145
Change :: Delete => ChangeKind :: Delete ,
152
146
}
@@ -157,7 +151,7 @@ impl ChangedFile {
157
151
#[ derive( Eq , PartialEq , Debug ) ]
158
152
pub enum Change {
159
153
/// The file was (re-)created
160
- Create ( Vec < u8 > ) ,
154
+ Create ( Vec < u8 > , u64 ) ,
161
155
/// The file was modified
162
156
Modify ( Vec < u8 > , u64 ) ,
163
157
/// The file was deleted
@@ -178,9 +172,7 @@ pub enum ChangeKind {
178
172
impl Vfs {
179
173
/// Id of the given path if it exists in the `Vfs` and is not deleted.
180
174
pub fn file_id ( & self , path : & VfsPath ) -> Option < FileId > {
181
- self . interner
182
- . get ( path)
183
- . filter ( |& it| matches ! ( self . get( it) , FileState :: Exists ( _) | FileState :: Created ) )
175
+ self . interner . get ( path) . filter ( |& it| matches ! ( self . get( it) , FileState :: Exists ( _) ) )
184
176
}
185
177
186
178
/// File path corresponding to the given `file_id`.
@@ -198,9 +190,7 @@ impl Vfs {
198
190
pub fn iter ( & self ) -> impl Iterator < Item = ( FileId , & VfsPath ) > + ' _ {
199
191
( 0 ..self . data . len ( ) )
200
192
. map ( |it| FileId ( it as u32 ) )
201
- . filter ( move |& file_id| {
202
- matches ! ( self . get( file_id) , FileState :: Exists ( _) | FileState :: Created )
203
- } )
193
+ . filter ( move |& file_id| matches ! ( self . get( file_id) , FileState :: Exists ( _) ) )
204
194
. map ( move |file_id| {
205
195
let path = self . interner . lookup ( file_id) ;
206
196
( file_id, path)
@@ -219,12 +209,11 @@ impl Vfs {
219
209
let state = self . get ( file_id) ;
220
210
let change_kind = match ( state, contents) {
221
211
( FileState :: Deleted , None ) => return false ,
222
- ( FileState :: Deleted , Some ( v) ) => Change :: Create ( v) ,
223
- ( FileState :: Exists ( _) | FileState :: Created , None ) => Change :: Delete ,
224
- ( FileState :: Created , Some ( v) ) => {
212
+ ( FileState :: Deleted , Some ( v) ) => {
225
213
let hash = hash_once :: < FxHasher > ( & * v) ;
226
- Change :: Modify ( v, hash)
214
+ Change :: Create ( v, hash)
227
215
}
216
+ ( FileState :: Exists ( _) , None ) => Change :: Delete ,
228
217
( FileState :: Exists ( hash) , Some ( v) ) => {
229
218
let new_hash = hash_once :: < FxHasher > ( & * v) ;
230
219
if new_hash == hash {
@@ -233,37 +222,61 @@ impl Vfs {
233
222
Change :: Modify ( v, new_hash)
234
223
}
235
224
} ;
236
- self . data [ file_id. 0 as usize ] = match change_kind {
237
- Change :: Create ( _) => {
238
- self . created_this_cycle . push ( file_id) ;
239
- FileState :: Created
240
- }
241
- // If the file got created this cycle, make sure we keep it that way even
242
- // if a modify comes in
243
- Change :: Modify ( _, _) if matches ! ( state, FileState :: Created ) => FileState :: Created ,
244
- Change :: Modify ( _, hash) => FileState :: Exists ( hash) ,
245
- Change :: Delete => FileState :: Deleted ,
225
+
226
+ let mut set_data = |change_kind| {
227
+ self . data [ file_id. 0 as usize ] = match change_kind {
228
+ & Change :: Create ( _, hash) | & Change :: Modify ( _, hash) => FileState :: Exists ( hash) ,
229
+ Change :: Delete => FileState :: Deleted ,
230
+ } ;
246
231
} ;
232
+
247
233
let changed_file = ChangedFile { file_id, change : change_kind } ;
248
- self . changes . push ( changed_file) ;
234
+ match self . changes . entry ( file_id) {
235
+ // two changes to the same file in one cycle, merge them appropriately
236
+ Entry :: Occupied ( mut o) => {
237
+ use Change :: * ;
238
+
239
+ match ( & mut o. get_mut ( ) . change , changed_file. change ) {
240
+ // newer `Delete` wins
241
+ ( change, Delete ) => * change = Delete ,
242
+ // merge `Create` with `Create` or `Modify`
243
+ ( Create ( prev, old_hash) , Create ( new, new_hash) | Modify ( new, new_hash) ) => {
244
+ * prev = new;
245
+ * old_hash = new_hash;
246
+ }
247
+ // collapse identical `Modify`es
248
+ ( Modify ( prev, old_hash) , Modify ( new, new_hash) ) => {
249
+ * prev = new;
250
+ * old_hash = new_hash;
251
+ }
252
+ // equivalent to `Modify`
253
+ ( change @ Delete , Create ( new, new_hash) ) => {
254
+ * change = Modify ( new, new_hash) ;
255
+ }
256
+ // shouldn't occur, but collapse into `Create`
257
+ ( change @ Delete , Modify ( new, new_hash) ) => {
258
+ stdx:: never!( ) ;
259
+ * change = Create ( new, new_hash) ;
260
+ }
261
+ // shouldn't occur, but keep the Create
262
+ ( prev @ Modify ( _, _) , new @ Create ( _, _) ) => * prev = new,
263
+ }
264
+ set_data ( & o. get ( ) . change ) ;
265
+ }
266
+ Entry :: Vacant ( v) => set_data ( & v. insert ( changed_file) . change ) ,
267
+ } ;
268
+
249
269
true
250
270
}
251
271
252
272
/// Drain and returns all the changes in the `Vfs`.
253
- pub fn take_changes ( & mut self ) -> Vec < ChangedFile > {
254
- let _p = span ! ( Level :: INFO , "Vfs::take_changes" ) . entered ( ) ;
255
- for file_id in self . created_this_cycle . drain ( ..) {
256
- if self . data [ file_id. 0 as usize ] == FileState :: Created {
257
- // downgrade the file from `Created` to `Exists` as the cycle is done
258
- self . data [ file_id. 0 as usize ] = FileState :: Exists ( todo ! ( ) ) ;
259
- }
260
- }
273
+ pub fn take_changes ( & mut self ) -> IndexMap < FileId , ChangedFile , BuildHasherDefault < FxHasher > > {
261
274
mem:: take ( & mut self . changes )
262
275
}
263
276
264
277
/// Provides a panic-less way to verify file_id validity.
265
278
pub fn exists ( & self , file_id : FileId ) -> bool {
266
- matches ! ( self . get( file_id) , FileState :: Exists ( _) | FileState :: Created )
279
+ matches ! ( self . get( file_id) , FileState :: Exists ( _) )
267
280
}
268
281
269
282
/// Returns the id associated with `path`
0 commit comments