@@ -46,14 +46,15 @@ pub mod loader;
4646mod path_interner;
4747mod vfs_path;
4848
49- use std:: { fmt, mem} ;
49+ use std:: { fmt, hash :: BuildHasherDefault , mem} ;
5050
5151use crate :: path_interner:: PathInterner ;
5252
5353pub use crate :: {
5454 anchored_path:: { AnchoredPath , AnchoredPathBuf } ,
5555 vfs_path:: VfsPath ,
5656} ;
57+ use indexmap:: { map:: Entry , IndexMap } ;
5758pub use paths:: { AbsPath , AbsPathBuf } ;
5859
5960use rustc_hash:: FxHasher ;
@@ -95,19 +96,12 @@ impl nohash_hasher::IsEnabled for FileId {}
9596pub struct Vfs {
9697 interner : PathInterner ,
9798 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 > > ,
104100}
105101
106102#[ derive( Copy , Clone , Debug , PartialEq , PartialOrd ) ]
107103pub 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.
111105 Exists ( u64 ) ,
112106 /// The file is deleted.
113107 Deleted ,
@@ -131,12 +125,12 @@ impl ChangedFile {
131125 /// Returns `true` if the change is [`Create`](ChangeKind::Create) or
132126 /// [`Delete`](Change::Delete).
133127 pub fn is_created_or_deleted ( & self ) -> bool {
134- matches ! ( self . change, Change :: Create ( _) | Change :: Delete )
128+ matches ! ( self . change, Change :: Create ( _, _ ) | Change :: Delete )
135129 }
136130
137131 /// Returns `true` if the change is [`Create`](ChangeKind::Create).
138132 pub fn is_created ( & self ) -> bool {
139- matches ! ( self . change, Change :: Create ( _) )
133+ matches ! ( self . change, Change :: Create ( _, _ ) )
140134 }
141135
142136 /// Returns `true` if the change is [`Modify`](ChangeKind::Modify).
@@ -146,7 +140,7 @@ impl ChangedFile {
146140
147141 pub fn kind ( & self ) -> ChangeKind {
148142 match self . change {
149- Change :: Create ( _) => ChangeKind :: Create ,
143+ Change :: Create ( _, _ ) => ChangeKind :: Create ,
150144 Change :: Modify ( _, _) => ChangeKind :: Modify ,
151145 Change :: Delete => ChangeKind :: Delete ,
152146 }
@@ -157,7 +151,7 @@ impl ChangedFile {
157151#[ derive( Eq , PartialEq , Debug ) ]
158152pub enum Change {
159153 /// The file was (re-)created
160- Create ( Vec < u8 > ) ,
154+ Create ( Vec < u8 > , u64 ) ,
161155 /// The file was modified
162156 Modify ( Vec < u8 > , u64 ) ,
163157 /// The file was deleted
@@ -178,9 +172,7 @@ pub enum ChangeKind {
178172impl Vfs {
179173 /// Id of the given path if it exists in the `Vfs` and is not deleted.
180174 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 ( _) ) )
184176 }
185177
186178 /// File path corresponding to the given `file_id`.
@@ -198,9 +190,7 @@ impl Vfs {
198190 pub fn iter ( & self ) -> impl Iterator < Item = ( FileId , & VfsPath ) > + ' _ {
199191 ( 0 ..self . data . len ( ) )
200192 . 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 ( _) ) )
204194 . map ( move |file_id| {
205195 let path = self . interner . lookup ( file_id) ;
206196 ( file_id, path)
@@ -219,12 +209,11 @@ impl Vfs {
219209 let state = self . get ( file_id) ;
220210 let change_kind = match ( state, contents) {
221211 ( 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) ) => {
225213 let hash = hash_once :: < FxHasher > ( & * v) ;
226- Change :: Modify ( v, hash)
214+ Change :: Create ( v, hash)
227215 }
216+ ( FileState :: Exists ( _) , None ) => Change :: Delete ,
228217 ( FileState :: Exists ( hash) , Some ( v) ) => {
229218 let new_hash = hash_once :: < FxHasher > ( & * v) ;
230219 if new_hash == hash {
@@ -233,37 +222,61 @@ impl Vfs {
233222 Change :: Modify ( v, new_hash)
234223 }
235224 } ;
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+ } ;
246231 } ;
232+
247233 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+
249269 true
250270 }
251271
252272 /// 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 > > {
261274 mem:: take ( & mut self . changes )
262275 }
263276
264277 /// Provides a panic-less way to verify file_id validity.
265278 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 ( _) )
267280 }
268281
269282 /// Returns the id associated with `path`
0 commit comments