From 8716c4cec3a05ba891b20b5f28df69d925b913ad Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 2 Oct 2020 15:45:09 +0200 Subject: Move ide::AnalysisChange -> base_db::Change This seems like a better factoring logically; ideally, clients shouldn't touch `set_` methods of the database directly. Additionally, I think this should remove the unfortunate duplication in fixture code. --- crates/base_db/src/change.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++ crates/base_db/src/lib.rs | 2 + 2 files changed, 99 insertions(+) create mode 100644 crates/base_db/src/change.rs (limited to 'crates/base_db') diff --git a/crates/base_db/src/change.rs b/crates/base_db/src/change.rs new file mode 100644 index 000000000..043e03bba --- /dev/null +++ b/crates/base_db/src/change.rs @@ -0,0 +1,97 @@ +//! Defines a unit of change that can applied to the database to get the next +//! state. Changes are transactional. + +use std::{fmt, sync::Arc}; + +use rustc_hash::FxHashSet; +use salsa::Durability; +use vfs::FileId; + +use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId}; + +/// Encapsulate a bunch of raw `.set` calls on the database. +#[derive(Default)] +pub struct Change { + pub roots: Option>, + pub files_changed: Vec<(FileId, Option>)>, + pub crate_graph: Option, +} + +impl fmt::Debug for Change { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let mut d = fmt.debug_struct("AnalysisChange"); + if let Some(roots) = &self.roots { + d.field("roots", roots); + } + if !self.files_changed.is_empty() { + d.field("files_changed", &self.files_changed.len()); + } + if self.crate_graph.is_some() { + d.field("crate_graph", &self.crate_graph); + } + d.finish() + } +} + +impl Change { + pub fn new() -> Change { + Change::default() + } + + pub fn set_roots(&mut self, roots: Vec) { + self.roots = Some(roots); + } + + pub fn change_file(&mut self, file_id: FileId, new_text: Option>) { + self.files_changed.push((file_id, new_text)) + } + + pub fn set_crate_graph(&mut self, graph: CrateGraph) { + self.crate_graph = Some(graph); + } + + pub fn apply(self, db: &mut dyn SourceDatabaseExt) { + let _p = profile::span("RootDatabase::apply_change"); + // db.request_cancellation(); + // log::info!("apply_change {:?}", change); + if let Some(roots) = self.roots { + let mut local_roots = FxHashSet::default(); + let mut library_roots = FxHashSet::default(); + for (idx, root) in roots.into_iter().enumerate() { + let root_id = SourceRootId(idx as u32); + let durability = durability(&root); + if root.is_library { + library_roots.insert(root_id); + } else { + local_roots.insert(root_id); + } + for file_id in root.iter() { + db.set_file_source_root_with_durability(file_id, root_id, durability); + } + db.set_source_root_with_durability(root_id, Arc::new(root), durability); + } + // db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); + // db.set_library_roots_with_durability(Arc::new(library_roots), Durability::HIGH); + } + + for (file_id, text) in self.files_changed { + let source_root_id = db.file_source_root(file_id); + let source_root = db.source_root(source_root_id); + let durability = durability(&source_root); + // XXX: can't actually remove the file, just reset the text + let text = text.unwrap_or_default(); + db.set_file_text_with_durability(file_id, text, durability) + } + if let Some(crate_graph) = self.crate_graph { + db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) + } + } +} + +fn durability(source_root: &SourceRoot) -> Durability { + if source_root.is_library { + Durability::HIGH + } else { + Durability::LOW + } +} diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index ee3415850..e38aa7257 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs @@ -1,6 +1,7 @@ //! base_db defines basic database traits. The concrete DB is defined by ide. mod cancellation; mod input; +mod change; pub mod fixture; use std::{panic, sync::Arc}; @@ -10,6 +11,7 @@ use syntax::{ast, Parse, SourceFile, TextRange, TextSize}; pub use crate::{ cancellation::Canceled, + change::Change, input::{ CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, ProcMacroId, SourceRoot, SourceRootId, -- cgit v1.2.3 From eeb27f95f1025128f8a1d24a515eb009498a1d44 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 2 Oct 2020 16:07:33 +0200 Subject: Rewrite fixtures on top of Change --- crates/base_db/src/fixture.rs | 173 ++++++++++++++++++++++-------------------- 1 file changed, 90 insertions(+), 83 deletions(-) (limited to 'crates/base_db') diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 5ff8ead0e..48fa73bf0 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs @@ -65,24 +65,26 @@ use test_utils::{extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER} use vfs::{file_set::FileSet, VfsPath}; use crate::{ - input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, SourceDatabaseExt, - SourceRoot, SourceRootId, + input::CrateName, Change, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, + SourceDatabaseExt, SourceRoot, SourceRootId, }; pub const WORKSPACE: SourceRootId = SourceRootId(0); pub trait WithFixture: Default + SourceDatabaseExt + 'static { fn with_single_file(text: &str) -> (Self, FileId) { + let fixture = ChangeFixture::parse(text); let mut db = Self::default(); - let (_, files) = with_files(&mut db, text); - assert_eq!(files.len(), 1); - (db, files[0]) + fixture.change.apply(&mut db); + assert_eq!(fixture.files.len(), 1); + (db, fixture.files[0]) } fn with_files(ra_fixture: &str) -> Self { + let fixture = ChangeFixture::parse(ra_fixture); let mut db = Self::default(); - let (pos, _) = with_files(&mut db, ra_fixture); - assert!(pos.is_none()); + fixture.change.apply(&mut db); + assert!(fixture.file_position.is_none()); db } @@ -96,9 +98,10 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { } fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) { + let fixture = ChangeFixture::parse(ra_fixture); let mut db = Self::default(); - let (pos, _) = with_files(&mut db, ra_fixture); - let (file_id, range_or_offset) = pos.unwrap(); + fixture.change.apply(&mut db); + let (file_id, range_or_offset) = fixture.file_position.unwrap(); (db, file_id, range_or_offset) } @@ -113,89 +116,93 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { impl WithFixture for DB {} -fn with_files( - db: &mut dyn SourceDatabaseExt, - fixture: &str, -) -> (Option<(FileId, RangeOrOffset)>, Vec) { - let fixture = Fixture::parse(fixture); - - let mut files = Vec::new(); - let mut crate_graph = CrateGraph::default(); - let mut crates = FxHashMap::default(); - let mut crate_deps = Vec::new(); - let mut default_crate_root: Option = None; - - let mut file_set = FileSet::default(); - let source_root_id = WORKSPACE; - let source_root_prefix = "/".to_string(); - let mut file_id = FileId(0); - - let mut file_position = None; - - for entry in fixture { - let text = if entry.text.contains(CURSOR_MARKER) { - let (range_or_offset, text) = extract_range_or_offset(&entry.text); - assert!(file_position.is_none()); - file_position = Some((file_id, range_or_offset)); - text.to_string() - } else { - entry.text.clone() - }; +pub struct ChangeFixture { + file_position: Option<(FileId, RangeOrOffset)>, + files: Vec, + change: Change, +} - let meta = FileMeta::from(entry); - assert!(meta.path.starts_with(&source_root_prefix)); +impl ChangeFixture { + fn parse(ra_fixture: &str) -> ChangeFixture { + let fixture = Fixture::parse(ra_fixture); + let mut change = Change::new(); + + let mut files = Vec::new(); + let mut crate_graph = CrateGraph::default(); + let mut crates = FxHashMap::default(); + let mut crate_deps = Vec::new(); + let mut default_crate_root: Option = None; + + let mut file_set = FileSet::default(); + let source_root_prefix = "/".to_string(); + let mut file_id = FileId(0); + + let mut file_position = None; + + for entry in fixture { + let text = if entry.text.contains(CURSOR_MARKER) { + let (range_or_offset, text) = extract_range_or_offset(&entry.text); + assert!(file_position.is_none()); + file_position = Some((file_id, range_or_offset)); + text.to_string() + } else { + entry.text.clone() + }; + + let meta = FileMeta::from(entry); + assert!(meta.path.starts_with(&source_root_prefix)); + + if let Some(krate) = meta.krate { + let crate_id = crate_graph.add_crate_root( + file_id, + meta.edition, + Some(krate.clone()), + meta.cfg, + meta.env, + Default::default(), + ); + let crate_name = CrateName::new(&krate).unwrap(); + let prev = crates.insert(crate_name.clone(), crate_id); + assert!(prev.is_none()); + for dep in meta.deps { + let dep = CrateName::new(&dep).unwrap(); + crate_deps.push((crate_name.clone(), dep)) + } + } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { + assert!(default_crate_root.is_none()); + default_crate_root = Some(file_id); + } + + change.change_file(file_id, Some(Arc::new(text))); + let path = VfsPath::new_virtual_path(meta.path); + file_set.insert(file_id, path.into()); + files.push(file_id); + file_id.0 += 1; + } - if let Some(krate) = meta.krate { - let crate_id = crate_graph.add_crate_root( - file_id, - meta.edition, - Some(krate.clone()), - meta.cfg, - meta.env, + if crates.is_empty() { + let crate_root = default_crate_root.unwrap(); + crate_graph.add_crate_root( + crate_root, + Edition::Edition2018, + None, + CfgOptions::default(), + Env::default(), Default::default(), ); - let crate_name = CrateName::new(&krate).unwrap(); - let prev = crates.insert(crate_name.clone(), crate_id); - assert!(prev.is_none()); - for dep in meta.deps { - let dep = CrateName::new(&dep).unwrap(); - crate_deps.push((crate_name.clone(), dep)) + } else { + for (from, to) in crate_deps { + let from_id = crates[&from]; + let to_id = crates[&to]; + crate_graph.add_dep(from_id, CrateName::new(&to).unwrap(), to_id).unwrap(); } - } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { - assert!(default_crate_root.is_none()); - default_crate_root = Some(file_id); } - db.set_file_text(file_id, Arc::new(text)); - db.set_file_source_root(file_id, source_root_id); - let path = VfsPath::new_virtual_path(meta.path); - file_set.insert(file_id, path.into()); - files.push(file_id); - file_id.0 += 1; - } + change.set_roots(vec![SourceRoot::new_local(file_set)]); + change.set_crate_graph(crate_graph); - if crates.is_empty() { - let crate_root = default_crate_root.unwrap(); - crate_graph.add_crate_root( - crate_root, - Edition::Edition2018, - None, - CfgOptions::default(), - Env::default(), - Default::default(), - ); - } else { - for (from, to) in crate_deps { - let from_id = crates[&from]; - let to_id = crates[&to]; - crate_graph.add_dep(from_id, CrateName::new(&to).unwrap(), to_id).unwrap(); - } + ChangeFixture { file_position, files, change } } - - db.set_source_root(source_root_id, Arc::new(SourceRoot::new_local(file_set))); - db.set_crate_graph(Arc::new(crate_graph)); - - (file_position, files) } struct FileMeta { -- cgit v1.2.3 From 09348b247465864c6462a39055803bcbb0156cfe Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 2 Oct 2020 16:13:48 +0200 Subject: Get rid of MockAnalysis --- crates/base_db/src/fixture.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'crates/base_db') diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 48fa73bf0..b7286fc7d 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs @@ -117,13 +117,13 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { impl WithFixture for DB {} pub struct ChangeFixture { - file_position: Option<(FileId, RangeOrOffset)>, - files: Vec, - change: Change, + pub file_position: Option<(FileId, RangeOrOffset)>, + pub files: Vec, + pub change: Change, } impl ChangeFixture { - fn parse(ra_fixture: &str) -> ChangeFixture { + pub fn parse(ra_fixture: &str) -> ChangeFixture { let fixture = Fixture::parse(ra_fixture); let mut change = Change::new(); @@ -132,6 +132,7 @@ impl ChangeFixture { let mut crates = FxHashMap::default(); let mut crate_deps = Vec::new(); let mut default_crate_root: Option = None; + let mut default_cfg = CfgOptions::default(); let mut file_set = FileSet::default(); let source_root_prefix = "/".to_string(); @@ -171,6 +172,7 @@ impl ChangeFixture { } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { assert!(default_crate_root.is_none()); default_crate_root = Some(file_id); + default_cfg = meta.cfg; } change.change_file(file_id, Some(Arc::new(text))); @@ -185,8 +187,8 @@ impl ChangeFixture { crate_graph.add_crate_root( crate_root, Edition::Edition2018, - None, - CfgOptions::default(), + Some("test".to_string()), + default_cfg, Env::default(), Default::default(), ); -- cgit v1.2.3