1
+ use itertools:: Itertools ;
1
2
use rustc_hash:: { FxHashMap , FxHashSet } ;
2
3
use std:: sync:: Arc ;
3
- use vfs:: { FileId , Vfs } ;
4
+ use vfs:: { AbsPathBuf , FileId , Vfs } ;
4
5
5
6
use super :: { ConfigInput , LocalConfigData , RootLocalConfigData } ;
6
7
@@ -15,12 +16,23 @@ pub enum ConfigTreeError {
15
16
16
17
/// Some rust-analyzer.toml files have changed, and/or the LSP client sent a new configuration.
17
18
pub struct ConfigChanges {
19
+ /// - `None` => no change
20
+ /// - `Some(None)` => the client config was removed / reset or something
21
+ /// - `Some(Some(...))` => the client config was updated
22
+ client_change : Option < Option < Arc < ConfigInput > > > ,
23
+ set_project_root : Option < AbsPathBuf > ,
24
+ set_source_roots : Option < FxHashSet < AbsPathBuf > > ,
18
25
ra_toml_changes : Vec < vfs:: ChangedFile > ,
26
+ }
27
+
28
+ /// Internal version
29
+ struct ConfigChangesInner {
19
30
/// - `None` => no change
20
31
/// - `Some(None)` => the client config was removed / reset or something
21
32
/// - `Some(Some(...))` => the client config was updated
22
33
client_change : Option < Option < Arc < ConfigInput > > > ,
23
34
parent_changes : FxHashMap < FileId , ConfigParent > ,
35
+ ra_toml_changes : Vec < vfs:: ChangedFile > ,
24
36
}
25
37
26
38
#[ derive( Debug ) ]
@@ -140,16 +152,20 @@ pub struct ConfigDb {
140
152
storage : salsa:: Storage < Self > ,
141
153
known_file_ids : FxHashSet < FileId > ,
142
154
xdg_config_file_id : FileId ,
155
+ source_roots : FxHashSet < AbsPathBuf > ,
156
+ project_root : AbsPathBuf ,
143
157
}
144
158
145
159
impl salsa:: Database for ConfigDb { }
146
160
147
161
impl ConfigDb {
148
- pub fn new ( xdg_config_file_id : FileId ) -> Self {
162
+ pub fn new ( xdg_config_file_id : FileId , project_root : AbsPathBuf ) -> Self {
149
163
let mut this = Self {
150
164
storage : Default :: default ( ) ,
151
165
known_file_ids : FxHashSet :: default ( ) ,
152
166
xdg_config_file_id,
167
+ source_roots : FxHashSet :: default ( ) ,
168
+ project_root,
153
169
} ;
154
170
this. set_client_config ( None ) ;
155
171
this. ensure_node ( xdg_config_file_id) ;
@@ -176,12 +192,68 @@ impl ConfigDb {
176
192
}
177
193
178
194
/// Applies a bunch of [`ConfigChanges`]. The FileIds referred to in `ConfigChanges` do not
179
- /// need to exist. You can generate the `parent_changes` hashmap by iterating ancestors of all
180
- /// of the [`ide::SourceRoot`]s, slapping `.map(|path| path.join("rust-analyzer.toml"))`.
181
- pub fn apply_changes ( & mut self , changes : ConfigChanges , vfs : & Vfs ) -> Vec < ConfigTreeError > {
195
+ /// need to exist.
196
+ pub fn apply_changes ( & mut self , changes : ConfigChanges , vfs : & mut Vfs ) -> Vec < ConfigTreeError > {
197
+ if let Some ( new_project_root) = & changes. set_project_root {
198
+ self . project_root = new_project_root. clone ( ) ;
199
+ }
200
+ let source_root_change = changes. set_source_roots . as_ref ( ) . or_else ( || {
201
+ if changes. set_project_root . is_some ( ) {
202
+ Some ( & self . source_roots )
203
+ } else {
204
+ None
205
+ }
206
+ } ) ;
207
+ let parent_changes = if let Some ( source_roots) = source_root_change {
208
+ source_roots
209
+ . iter ( )
210
+ . flat_map ( |path : & AbsPathBuf | {
211
+ path. ancestors ( )
212
+ . take_while ( |x| x. starts_with ( & self . project_root ) )
213
+ . map ( |dir| dir. join ( "rust-analyzer.toml" ) )
214
+ . map ( |path| vfs. alloc_file_id ( path. into ( ) ) )
215
+ . collect_vec ( )
216
+ // immediately get tuple_windows before returning from flat_map
217
+ . into_iter ( )
218
+ . tuple_windows ( )
219
+ . map ( |( a, b) | ( a, ConfigParent :: Parent ( b) ) )
220
+ } )
221
+ . collect :: < FxHashMap < _ , _ > > ( )
222
+ } else {
223
+ Default :: default ( )
224
+ } ;
225
+
226
+ if tracing:: enabled!( tracing:: Level :: TRACE ) {
227
+ for ( & a, parent) in & parent_changes {
228
+ tracing:: trace!(
229
+ "{a:?} ({:?}): parent = {parent:?} ({:?})" ,
230
+ vfs. file_path( a) ,
231
+ match parent {
232
+ ConfigParent :: Parent ( p) => vfs. file_path( * p) . to_string( ) ,
233
+ ConfigParent :: UserDefault => "xdg" . to_string( ) ,
234
+ }
235
+ ) ;
236
+ }
237
+ }
238
+
239
+ // Could delete (self.known_file_ids - parent_changes.keys) here.
240
+
241
+ let inner = ConfigChangesInner {
242
+ ra_toml_changes : changes. ra_toml_changes ,
243
+ client_change : changes. client_change ,
244
+ parent_changes,
245
+ } ;
246
+ self . apply_changes_inner ( inner, vfs)
247
+ }
248
+
249
+ fn apply_changes_inner (
250
+ & mut self ,
251
+ changes : ConfigChangesInner ,
252
+ vfs : & Vfs ,
253
+ ) -> Vec < ConfigTreeError > {
182
254
let mut scratch_errors = Vec :: new ( ) ;
183
255
let mut errors = Vec :: new ( ) ;
184
- let ConfigChanges { client_change, ra_toml_changes, parent_changes } = changes;
256
+ let ConfigChangesInner { client_change, ra_toml_changes, parent_changes } = changes;
185
257
186
258
if let Some ( change) = client_change {
187
259
let current = self . client_config ( ) ;
@@ -285,7 +357,7 @@ mod tests {
285
357
use std:: path:: { Path , PathBuf } ;
286
358
287
359
use itertools:: Itertools ;
288
- use vfs:: { AbsPathBuf , VfsPath } ;
360
+ use vfs:: { AbsPath , AbsPathBuf , VfsPath } ;
289
361
290
362
fn alloc_file_id ( vfs : & mut Vfs , s : & str ) -> FileId {
291
363
let abs_path = AbsPathBuf :: try_from ( PathBuf :: new ( ) . join ( s) ) . unwrap ( ) ;
@@ -310,11 +382,14 @@ mod tests {
310
382
fn basic ( ) {
311
383
tracing_subscriber:: fmt ( ) . try_init ( ) . ok ( ) ;
312
384
let mut vfs = Vfs :: default ( ) ;
385
+ let project_root = AbsPath :: assert ( Path :: new ( "/root" ) ) ;
313
386
let xdg_config_file_id =
314
387
alloc_file_id ( & mut vfs, "/home/username/.config/rust-analyzer/rust-analyzer.toml" ) ;
315
- let mut config_tree = ConfigDb :: new ( xdg_config_file_id) ;
388
+ let mut config_tree = ConfigDb :: new ( xdg_config_file_id, project_root. to_path_buf ( ) ) ;
389
+
390
+ let source_roots = [ "/root/crate_a" ] . map ( Path :: new) . map ( AbsPath :: assert) ;
316
391
317
- let root = alloc_config (
392
+ let _root = alloc_config (
318
393
& mut vfs,
319
394
"/root/rust-analyzer.toml" ,
320
395
r#"
@@ -335,13 +410,12 @@ mod tests {
335
410
"# ,
336
411
) ;
337
412
338
- let mut parent_changes = FxHashMap :: default ( ) ;
339
- parent_changes. insert ( crate_a, ConfigParent :: Parent ( root) ) ;
340
-
413
+ let new_source_roots = source_roots. into_iter ( ) . map ( |abs| abs. to_path_buf ( ) ) . collect ( ) ;
341
414
let changes = ConfigChanges {
342
415
// Normally you will filter these!
343
416
ra_toml_changes : vfs. take_changes ( ) ,
344
- parent_changes,
417
+ set_project_root : None ,
418
+ set_source_roots : Some ( new_source_roots) ,
345
419
client_change : Some ( Some ( Arc :: new ( ConfigInput {
346
420
local : crate :: config:: LocalConfigInput {
347
421
semanticHighlighting_strings_enable : Some ( false ) ,
@@ -351,7 +425,7 @@ mod tests {
351
425
} ) ) ) ,
352
426
} ;
353
427
354
- dbg ! ( config_tree. apply_changes( changes, & vfs) ) ;
428
+ dbg ! ( config_tree. apply_changes( changes, & mut vfs) ) ;
355
429
356
430
let local = config_tree. local_config ( crate_a) ;
357
431
// from root
@@ -384,11 +458,12 @@ mod tests {
384
458
) ;
385
459
386
460
let changes = ConfigChanges {
387
- ra_toml_changes : dbg ! ( vfs. take_changes( ) ) ,
388
- parent_changes : Default :: default ( ) ,
389
461
client_change : None ,
462
+ set_project_root : None ,
463
+ set_source_roots : None ,
464
+ ra_toml_changes : dbg ! ( vfs. take_changes( ) ) ,
390
465
} ;
391
- dbg ! ( config_tree. apply_changes( changes, & vfs) ) ;
466
+ dbg ! ( config_tree. apply_changes( changes, & mut vfs) ) ;
392
467
393
468
let prev = local;
394
469
let local = config_tree. local_config ( crate_a) ;
@@ -410,67 +485,43 @@ mod tests {
410
485
}
411
486
412
487
#[ test]
413
- fn generated_parent_changes ( ) {
488
+ fn set_source_roots ( ) {
414
489
tracing_subscriber:: fmt ( ) . try_init ( ) . ok ( ) ;
415
490
let mut vfs = Vfs :: default ( ) ;
416
491
492
+ let project_root = AbsPath :: assert ( Path :: new ( "/root" ) ) ;
417
493
let xdg =
418
494
alloc_file_id ( & mut vfs, "/home/username/.config/rust-analyzer/rust-analyzer.toml" ) ;
419
- let mut config_tree = ConfigDb :: new ( xdg) ;
495
+ let mut config_tree = ConfigDb :: new ( xdg, project_root . to_path_buf ( ) ) ;
420
496
421
- let project_root = Path :: new ( "/root" ) ;
422
- let sourceroots =
423
- [ PathBuf :: new ( ) . join ( "/root/crate_a" ) , PathBuf :: new ( ) . join ( "/root/crate_a/crate_b" ) ] ;
424
- let sourceroot_tomls = sourceroots
497
+ let source_roots =
498
+ [ "/root/crate_a" , "/root/crate_a/crate_b" ] . map ( Path :: new) . map ( AbsPath :: assert) ;
499
+ let source_root_tomls = source_roots
425
500
. iter ( )
426
501
. map ( |dir| dir. join ( "rust-analyzer.toml" ) )
427
502
. map ( |path| AbsPathBuf :: try_from ( path) . unwrap ( ) )
428
503
. map ( |path| vfs. alloc_file_id ( path. into ( ) ) )
429
504
. collect_vec ( ) ;
430
- let & [ crate_a, crate_b] = & sourceroot_tomls [ ..] else {
505
+ let & [ crate_a, crate_b] = & source_root_tomls [ ..] else {
431
506
panic ! ( ) ;
432
507
} ;
433
508
434
- let parent_changes = sourceroots
435
- . iter ( )
436
- . flat_map ( |path| {
437
- path. ancestors ( )
438
- . take_while ( |x| x. starts_with ( project_root) )
439
- . map ( |dir| dir. join ( "rust-analyzer.toml" ) )
440
- . map ( |path| AbsPathBuf :: try_from ( path) . unwrap ( ) )
441
- . map ( |path| vfs. alloc_file_id ( path. into ( ) ) )
442
- . collect_vec ( )
443
- . into_iter ( )
444
- . tuple_windows ( )
445
- . map ( |( a, b) | ( a, ConfigParent :: Parent ( b) ) )
446
- } )
447
- . collect :: < FxHashMap < _ , _ > > ( ) ;
448
-
449
- for ( & a, parent) in & parent_changes {
450
- eprintln ! (
451
- "{a:?} ({:?}): parent = {parent:?} ({:?})" ,
452
- vfs. file_path( a) ,
453
- match parent {
454
- ConfigParent :: Parent ( p) => vfs. file_path( * p) . to_string( ) ,
455
- ConfigParent :: UserDefault => "xdg" . to_string( ) ,
456
- }
457
- ) ;
458
- }
459
-
460
509
vfs. set_file_id_contents (
461
510
xdg,
462
511
Some ( b"[inlayHints.discriminantHints]\n enable = \" always\" " . to_vec ( ) ) ,
463
512
) ;
464
513
vfs. set_file_id_contents ( crate_a, Some ( b"[completion.autoself]\n enable = false" . to_vec ( ) ) ) ;
465
514
// note that crate_b's rust-analyzer.toml doesn't exist
466
515
516
+ let new_source_roots = source_roots. into_iter ( ) . map ( |abs| abs. to_path_buf ( ) ) . collect ( ) ;
467
517
let changes = ConfigChanges {
468
- ra_toml_changes : dbg ! ( vfs. take_changes( ) ) ,
469
- parent_changes,
470
518
client_change : None ,
519
+ set_project_root : None , // already set in ConfigDb::new(...)
520
+ set_source_roots : Some ( new_source_roots) ,
521
+ ra_toml_changes : dbg ! ( vfs. take_changes( ) ) ,
471
522
} ;
472
523
473
- dbg ! ( config_tree. apply_changes( changes, & vfs) ) ;
524
+ dbg ! ( config_tree. apply_changes( changes, & mut vfs) ) ;
474
525
let local = config_tree. local_config ( crate_b) ;
475
526
476
527
assert_eq ! (
0 commit comments