@@ -2,7 +2,7 @@ use indextree::NodeId;
2
2
use parking_lot:: { RwLock , RwLockUpgradableReadGuard } ;
3
3
use rustc_hash:: FxHashMap ;
4
4
use slotmap:: SlotMap ;
5
- use std:: sync:: Arc ;
5
+ use std:: { fmt , sync:: Arc } ;
6
6
use vfs:: { FileId , Vfs } ;
7
7
8
8
use super :: { ConfigInput , LocalConfigData , RootLocalConfigData } ;
@@ -13,6 +13,19 @@ pub struct ConcurrentConfigTree {
13
13
rwlock : RwLock < ConfigTree > ,
14
14
}
15
15
16
+ impl ConcurrentConfigTree {
17
+ fn new ( config_tree : ConfigTree ) -> Self {
18
+ Self { rwlock : RwLock :: new ( config_tree) }
19
+ }
20
+ }
21
+
22
+ impl fmt:: Debug for ConcurrentConfigTree {
23
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
24
+ self . rwlock . read ( ) . fmt ( f)
25
+ }
26
+ }
27
+
28
+ #[ derive( Debug ) ]
16
29
pub enum ConfigTreeError {
17
30
Removed ,
18
31
NonExistent ,
@@ -35,12 +48,14 @@ pub struct ConfigChanges {
35
48
parent_changes : Vec < ConfigParentChange > ,
36
49
}
37
50
51
+ #[ derive( Debug ) ]
38
52
pub struct ConfigParentChange {
39
53
/// The config node in question
40
- pub node : FileId ,
54
+ pub file_id : FileId ,
41
55
pub parent : ConfigParent ,
42
56
}
43
57
58
+ #[ derive( Debug ) ]
44
59
pub enum ConfigParent {
45
60
/// The node is now a root in its own right, but still inherits from the config in XDG_CONFIG_HOME
46
61
/// etc
@@ -100,6 +115,7 @@ slotmap::new_key_type! {
100
115
struct ComputedIdx ;
101
116
}
102
117
118
+ #[ derive( Debug ) ]
103
119
struct ConfigNode {
104
120
src : ConfigSource ,
105
121
input : Option < Arc < ConfigInput > > ,
@@ -190,6 +206,7 @@ impl ConfigTree {
190
206
let self_computed = if let Some ( parent) =
191
207
self . tree . get ( node_id) . ok_or ( ConfigTreeError :: NonExistent ) ?. parent ( )
192
208
{
209
+ tracing:: trace!( "looking at parent of {node_id:?} -> {parent:?}" ) ;
193
210
let self_input = node. input . clone ( ) ;
194
211
let parent_computed = self . compute_inner ( parent) ?;
195
212
if let Some ( input) = self_input. as_deref ( ) {
@@ -198,6 +215,7 @@ impl ConfigTree {
198
215
parent_computed
199
216
}
200
217
} else {
218
+ tracing:: trace!( "{node_id:?} is a root node" ) ;
201
219
// We have hit a root node
202
220
let self_input = node. input . clone ( ) ;
203
221
if let Some ( input) = self_input. as_deref ( ) {
@@ -218,17 +236,20 @@ impl ConfigTree {
218
236
let computed = self . computed . insert ( None ) ;
219
237
let node =
220
238
self . tree . new_node ( ConfigNode { src : ConfigSource :: RaToml ( file_id) , input, computed } ) ;
221
- self . ra_file_id_map . insert ( file_id, node) ;
239
+ if let Some ( _removed) = self . ra_file_id_map . insert ( file_id, node) {
240
+ panic ! ( "ERROR: node should not have existed for {file_id:?} but it did" ) ;
241
+ }
222
242
node
223
243
}
224
244
225
245
fn update_toml (
226
246
& mut self ,
227
247
file_id : FileId ,
228
248
input : Option < Arc < ConfigInput > > ,
229
- ) -> Result < ( ) , ConfigTreeError > {
249
+ ) -> Result < NodeId , ConfigTreeError > {
230
250
let Some ( node_id) = self . ra_file_id_map . get ( & file_id) . cloned ( ) else {
231
- return Err ( ConfigTreeError :: NonExistent ) ;
251
+ let node_id = self . insert_toml ( file_id, input) ;
252
+ return Ok ( node_id) ;
232
253
} ;
233
254
if node_id. is_removed ( & self . tree ) {
234
255
return Err ( ConfigTreeError :: Removed ) ;
@@ -237,7 +258,14 @@ impl ConfigTree {
237
258
node. get_mut ( ) . input = input;
238
259
239
260
self . invalidate_subtree ( node_id) ;
240
- Ok ( ( ) )
261
+ Ok ( node_id)
262
+ }
263
+
264
+ fn ensure_node ( & mut self , file_id : FileId ) -> NodeId {
265
+ let Some ( & node_id) = self . ra_file_id_map . get ( & file_id) else {
266
+ return self . insert_toml ( file_id, None ) ;
267
+ } ;
268
+ node_id
241
269
}
242
270
243
271
fn invalidate_subtree ( & mut self , node_id : NodeId ) {
@@ -253,20 +281,19 @@ impl ConfigTree {
253
281
}
254
282
255
283
fn remove_toml ( & mut self , file_id : FileId ) -> Option < ( ) > {
256
- let node_id = self . ra_file_id_map . remove ( & file_id) ?;
284
+ let node_id = * self . ra_file_id_map . get ( & file_id) ?;
257
285
if node_id. is_removed ( & self . tree ) {
258
286
return None ;
259
287
}
260
- let node = self . tree . get ( node_id) ?;
261
- let idx = node. get ( ) . computed ;
262
- let _ = self . computed . remove ( idx) ;
288
+ let node = self . tree . get_mut ( node_id) ?. get_mut ( ) ;
289
+ node. input = None ;
263
290
self . invalidate_subtree ( node_id) ;
264
291
Some ( ( ) )
265
292
}
266
293
267
294
fn apply_changes (
268
295
& mut self ,
269
- mut changes : ConfigChanges ,
296
+ changes : ConfigChanges ,
270
297
vfs : & Vfs ,
271
298
errors : & mut Vec < ConfigTreeError > ,
272
299
) {
@@ -287,12 +314,23 @@ impl ConfigTree {
287
314
self . invalidate_subtree ( self . xdg_config_node_id ) ;
288
315
}
289
316
317
+ for ConfigParentChange { file_id, parent } in parent_changes {
318
+ let node_id = self . ensure_node ( file_id) ;
319
+ let parent_node_id = match parent {
320
+ ConfigParent :: UserDefault => self . xdg_config_node_id ,
321
+ ConfigParent :: Parent ( parent_file_id) => self . ensure_node ( parent_file_id) ,
322
+ } ;
323
+ // order of children within the parent node does not matter
324
+ tracing:: trace!( "appending child {node_id:?} to {parent_node_id:?}" ) ;
325
+ parent_node_id. append ( node_id, & mut self . tree ) ;
326
+ }
327
+
290
328
for change in ra_toml_changes {
291
329
// turn and face the strain
292
330
match change. change_kind {
293
331
vfs:: ChangeKind :: Create => {
294
332
let input = parse_toml ( change. file_id , vfs, & mut scratch_errors, errors) ;
295
- let _new_node = self . insert_toml ( change. file_id , input) ;
333
+ let _new_node = self . update_toml ( change. file_id , input) ;
296
334
}
297
335
vfs:: ChangeKind :: Modify => {
298
336
let input = parse_toml ( change. file_id , vfs, & mut scratch_errors, errors) ;
@@ -307,3 +345,85 @@ impl ConfigTree {
307
345
}
308
346
}
309
347
}
348
+
349
+ impl fmt:: Debug for ConfigTree {
350
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
351
+ self . tree . fmt ( f)
352
+ }
353
+ }
354
+
355
+ #[ cfg( test) ]
356
+ mod tests {
357
+ use std:: path:: PathBuf ;
358
+
359
+ use vfs:: { AbsPathBuf , VfsPath } ;
360
+
361
+ fn alloc_file_id ( vfs : & mut Vfs , s : & str ) -> FileId {
362
+ let abs_path = AbsPathBuf :: try_from ( PathBuf :: new ( ) . join ( s) ) . unwrap ( ) ;
363
+
364
+ let vfs_path = VfsPath :: from ( abs_path) ;
365
+ // FIXME: the vfs should expose this functionality more simply.
366
+ // We shouldn't have to clone the vfs path just to get a FileId.
367
+ let file_id = vfs. alloc_file_id ( vfs_path) ;
368
+ vfs. set_file_id_contents ( file_id, None ) ;
369
+ file_id
370
+ }
371
+
372
+ fn alloc_config ( vfs : & mut Vfs , s : & str , config : & str ) -> FileId {
373
+ let abs_path = AbsPathBuf :: try_from ( PathBuf :: new ( ) . join ( s) ) . unwrap ( ) ;
374
+
375
+ let vfs_path = VfsPath :: from ( abs_path) ;
376
+ // FIXME: the vfs should expose this functionality more simply.
377
+ // We shouldn't have to clone the vfs path just to get a FileId.
378
+ let file_id = vfs. alloc_file_id ( vfs_path) ;
379
+ vfs. set_file_id_contents ( file_id, Some ( config. to_string ( ) . into_bytes ( ) ) ) ;
380
+ file_id
381
+ }
382
+
383
+ use super :: * ;
384
+ #[ test]
385
+ fn basic ( ) {
386
+ let mut vfs = Vfs :: default ( ) ;
387
+ let xdg_config_file_id =
388
+ alloc_file_id ( & mut vfs, "/home/.config/rust-analyzer/rust-analyzer.toml" ) ;
389
+ let config_tree = ConcurrentConfigTree :: new ( ConfigTree :: new ( xdg_config_file_id) ) ;
390
+
391
+ let root = alloc_config (
392
+ & mut vfs,
393
+ "/root/rust-analyzer.toml" ,
394
+ r#"
395
+ [completion.autoself]
396
+ enable = false
397
+ "# ,
398
+ ) ;
399
+
400
+ let crate_a = alloc_config (
401
+ & mut vfs,
402
+ "/root/crate_a/rust-analyzer.toml" ,
403
+ r#"
404
+ [completion.autoimport]
405
+ enable = false
406
+ "# ,
407
+ ) ;
408
+
409
+ let parent_changes =
410
+ vec ! [ ConfigParentChange { file_id: crate_a, parent: ConfigParent :: Parent ( root) } ] ;
411
+
412
+ let changes = ConfigChanges {
413
+ // Normally you will filter these!
414
+ ra_toml_changes : vfs. take_changes ( ) ,
415
+ parent_changes,
416
+ xdg_config_change : None ,
417
+ client_change : None ,
418
+ } ;
419
+
420
+ dbg ! ( config_tree. apply_changes( changes, & vfs) ) ;
421
+ dbg ! ( & config_tree) ;
422
+
423
+ let local = config_tree. read_config ( crate_a) . unwrap ( ) ;
424
+ // from root
425
+ assert_eq ! ( local. completion_autoself_enable, false ) ;
426
+ // from crate_a
427
+ assert_eq ! ( local. completion_autoimport_enable, false ) ;
428
+ }
429
+ }
0 commit comments