Skip to content

Commit 18ff646

Browse files
committed
config tree gets a tree shape
1 parent c442ea1 commit 18ff646

File tree

2 files changed

+143
-14
lines changed

2 files changed

+143
-14
lines changed

crates/rust-analyzer/src/config/tree.rs

Lines changed: 132 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use indextree::NodeId;
22
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
33
use rustc_hash::FxHashMap;
44
use slotmap::SlotMap;
5-
use std::sync::Arc;
5+
use std::{fmt, sync::Arc};
66
use vfs::{FileId, Vfs};
77

88
use super::{ConfigInput, LocalConfigData, RootLocalConfigData};
@@ -13,6 +13,19 @@ pub struct ConcurrentConfigTree {
1313
rwlock: RwLock<ConfigTree>,
1414
}
1515

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)]
1629
pub enum ConfigTreeError {
1730
Removed,
1831
NonExistent,
@@ -35,12 +48,14 @@ pub struct ConfigChanges {
3548
parent_changes: Vec<ConfigParentChange>,
3649
}
3750

51+
#[derive(Debug)]
3852
pub struct ConfigParentChange {
3953
/// The config node in question
40-
pub node: FileId,
54+
pub file_id: FileId,
4155
pub parent: ConfigParent,
4256
}
4357

58+
#[derive(Debug)]
4459
pub enum ConfigParent {
4560
/// The node is now a root in its own right, but still inherits from the config in XDG_CONFIG_HOME
4661
/// etc
@@ -100,6 +115,7 @@ slotmap::new_key_type! {
100115
struct ComputedIdx;
101116
}
102117

118+
#[derive(Debug)]
103119
struct ConfigNode {
104120
src: ConfigSource,
105121
input: Option<Arc<ConfigInput>>,
@@ -190,6 +206,7 @@ impl ConfigTree {
190206
let self_computed = if let Some(parent) =
191207
self.tree.get(node_id).ok_or(ConfigTreeError::NonExistent)?.parent()
192208
{
209+
tracing::trace!("looking at parent of {node_id:?} -> {parent:?}");
193210
let self_input = node.input.clone();
194211
let parent_computed = self.compute_inner(parent)?;
195212
if let Some(input) = self_input.as_deref() {
@@ -198,6 +215,7 @@ impl ConfigTree {
198215
parent_computed
199216
}
200217
} else {
218+
tracing::trace!("{node_id:?} is a root node");
201219
// We have hit a root node
202220
let self_input = node.input.clone();
203221
if let Some(input) = self_input.as_deref() {
@@ -218,17 +236,20 @@ impl ConfigTree {
218236
let computed = self.computed.insert(None);
219237
let node =
220238
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+
}
222242
node
223243
}
224244

225245
fn update_toml(
226246
&mut self,
227247
file_id: FileId,
228248
input: Option<Arc<ConfigInput>>,
229-
) -> Result<(), ConfigTreeError> {
249+
) -> Result<NodeId, ConfigTreeError> {
230250
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);
232253
};
233254
if node_id.is_removed(&self.tree) {
234255
return Err(ConfigTreeError::Removed);
@@ -237,7 +258,14 @@ impl ConfigTree {
237258
node.get_mut().input = input;
238259

239260
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
241269
}
242270

243271
fn invalidate_subtree(&mut self, node_id: NodeId) {
@@ -253,20 +281,19 @@ impl ConfigTree {
253281
}
254282

255283
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)?;
257285
if node_id.is_removed(&self.tree) {
258286
return None;
259287
}
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;
263290
self.invalidate_subtree(node_id);
264291
Some(())
265292
}
266293

267294
fn apply_changes(
268295
&mut self,
269-
mut changes: ConfigChanges,
296+
changes: ConfigChanges,
270297
vfs: &Vfs,
271298
errors: &mut Vec<ConfigTreeError>,
272299
) {
@@ -287,12 +314,23 @@ impl ConfigTree {
287314
self.invalidate_subtree(self.xdg_config_node_id);
288315
}
289316

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+
290328
for change in ra_toml_changes {
291329
// turn and face the strain
292330
match change.change_kind {
293331
vfs::ChangeKind::Create => {
294332
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);
296334
}
297335
vfs::ChangeKind::Modify => {
298336
let input = parse_toml(change.file_id, vfs, &mut scratch_errors, errors);
@@ -307,3 +345,85 @@ impl ConfigTree {
307345
}
308346
}
309347
}
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+
}

crates/vfs/src/lib.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ impl Vfs {
133133
self.get(file_id).as_deref().unwrap()
134134
}
135135

136+
/// File content, but returns None if the file was deleted.
137+
pub fn file_contents_opt(&self, file_id: FileId) -> Option<&[u8]> {
138+
self.get(file_id).as_deref()
139+
}
140+
136141
/// Returns the overall memory usage for the stored files.
137142
pub fn memory_usage(&self) -> usize {
138143
self.data.iter().flatten().map(|d| d.capacity()).sum()
@@ -157,8 +162,12 @@ impl Vfs {
157162
///
158163
/// If the path does not currently exists in the `Vfs`, allocates a new
159164
/// [`FileId`] for it.
160-
pub fn set_file_contents(&mut self, path: VfsPath, mut contents: Option<Vec<u8>>) -> bool {
165+
pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool {
161166
let file_id = self.alloc_file_id(path);
167+
self.set_file_id_contents(file_id, contents)
168+
}
169+
170+
pub fn set_file_id_contents(&mut self, file_id: FileId, mut contents: Option<Vec<u8>>) -> bool {
162171
let change_kind = match (self.get(file_id), &contents) {
163172
(None, None) => return false,
164173
(Some(old), Some(new)) if old == new => return false,
@@ -196,7 +205,7 @@ impl Vfs {
196205
/// - Else, returns `path`'s id.
197206
///
198207
/// Does not record a change.
199-
fn alloc_file_id(&mut self, path: VfsPath) -> FileId {
208+
pub fn alloc_file_id(&mut self, path: VfsPath) -> FileId {
200209
let file_id = self.interner.intern(path);
201210
let idx = file_id.0 as usize;
202211
let len = self.data.len().max(idx + 1);

0 commit comments

Comments
 (0)