diff --git a/crates/common/src/core_ingot.rs b/crates/common/src/core_ingot.rs new file mode 100644 index 0000000000..66c83a9e01 --- /dev/null +++ b/crates/common/src/core_ingot.rs @@ -0,0 +1,79 @@ +use camino::Utf8PathBuf; +use rust_embed::Embed; + +use crate::{ + indexmap::IndexSet, + input::{IngotKind, Version}, + InputDb, InputFile, InputIngot, +}; +#[derive(Embed)] +#[folder = "../../library/core"] +struct Core; + +#[salsa::tracked] +pub fn core(db: &dyn InputDb) -> InputIngot { + let mut files = IndexSet::new(); + let mut root_file = None; + let ingot_path = Utf8PathBuf::from("core"); + + for file in Core::iter() { + if file.ends_with(".fe") { + let path = ingot_path.join(Utf8PathBuf::from(&file)); + if let Some(content) = Core::get(&file) { + let is_root = path == "core/src/lib.fe"; + let input_file = InputFile::__new_impl( + db, + path, + String::from_utf8(content.data.into_owned()).unwrap(), + ); + if is_root { + root_file = Some(input_file); + } + files.insert(input_file); + } + } + } + + if root_file.is_none() { + panic!("root file missing from core") + } + + InputIngot::__new_impl( + db, + ingot_path, + IngotKind::Core, + Version::new(0, 0, 0), + IndexSet::default(), + files, + root_file, + ) +} + +#[cfg(test)] +mod tests { + use crate::impl_db_traits; + + use super::*; + + #[derive(Clone, Default)] + #[salsa::db] + pub(crate) struct TestDb { + storage: salsa::Storage, + } + impl_db_traits!(TestDb, InputDb); + + #[test] + fn is_core_deduplicated() { + // this is a sanity check + let mut db = TestDb::default(); + let core_1 = core(&db); + let core_2 = core(&db); + + let foo = InputFile::new(&db, "src/mod1/foo.fe".into(), core_1); + + core_2.set_root_file(&mut db, foo); + + assert!(core_1.eq(&core_2)); + assert!(core_1.root_file(&db).eq(&core_2.root_file(&db))); + } +} diff --git a/crates/common/src/input.rs b/crates/common/src/input.rs index 0c35923406..543b687f62 100644 --- a/crates/common/src/input.rs +++ b/crates/common/src/input.rs @@ -1,15 +1,8 @@ -use core::panic; - use camino::Utf8PathBuf; -use rust_embed::Embed; use salsa::Setter; use smol_str::SmolStr; -use crate::{indexmap::IndexSet, InputDb}; - -#[derive(Embed)] -#[folder = "../../library/core"] -struct Core; +use crate::{core_ingot, indexmap::IndexSet, InputDb}; /// An ingot is a collection of files which are compiled together. /// Ingot can depend on other ingots. @@ -40,6 +33,7 @@ pub struct InputIngot { #[get(__get_root_file_impl)] root_file: Option, } + impl InputIngot { pub fn new( db: &dyn InputDb, @@ -62,41 +56,7 @@ impl InputIngot { } pub fn core(db: &dyn InputDb) -> InputIngot { - let mut files = IndexSet::new(); - let mut root_file = None; - let ingot_path = Utf8PathBuf::from("core"); - - for file in Core::iter() { - if file.ends_with(".fe") { - let path = ingot_path.join(Utf8PathBuf::from(&file)); - if let Some(content) = Core::get(&file) { - let is_root = path == "core/src/lib.fe"; - let input_file = InputFile::new( - db, - path, - String::from_utf8(content.data.into_owned()).unwrap(), - ); - if is_root { - root_file = Some(input_file); - } - files.insert(input_file); - } - } - } - - if root_file.is_none() { - panic!("root file missing from core") - } - - Self::__new_impl( - db, - ingot_path, - IngotKind::Core, - Version::new(0, 0, 0), - IndexSet::default(), - files, - root_file, - ) + core_ingot::core(db) } /// Set the root file of the ingot. @@ -118,10 +78,22 @@ impl InputIngot { } } -#[salsa::input] +#[salsa::interned] +pub struct FilePath<'db> { + path: Utf8PathBuf, +} + +impl<'db> FilePath<'db> { + pub fn from(db: &'db dyn InputDb, path: impl Into) -> Self { + FilePath::new(db, path.into()) + } +} + +#[salsa::input(constructor = __new_impl)] pub struct InputFile { /// A path to the file from the ingot root directory. #[return_ref] + #[set(__set_path_impl)] pub path: Utf8PathBuf, #[return_ref] @@ -132,6 +104,27 @@ impl InputFile { pub fn abs_path(&self, db: &dyn InputDb, ingot: InputIngot) -> Utf8PathBuf { ingot.path(db).join(self.path(db)) } + + pub fn new(db: &dyn InputDb, path: Utf8PathBuf, ingot: InputIngot) -> Self { + input_for_file_path(db, FilePath::from(db, path), ingot) + } +} + +#[salsa::tracked] +pub fn input_for_file_path<'db>( + db: &'db dyn InputDb, + path: FilePath<'db>, + ingot: InputIngot, +) -> InputFile { + // Check if the ingot already has a file with the same path + for file in ingot.files(db).iter() { + if file.path(db).as_path() == path.path(db) { + return *file; + } + } + + // If no existing file is found, create a new one + InputFile::__new_impl(db, path.path(db), String::new()) } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -178,3 +171,31 @@ impl Ord for IngotDependency { } pub type Version = semver::Version; + +#[cfg(test)] +mod tests { + use crate::{impl_db_traits, input::*}; + + #[derive(Default, Clone)] + #[salsa::db] + pub struct TestDatabase { + storage: salsa::Storage, + } + + impl_db_traits!(TestDatabase, InputDb); + + #[test] + fn test_input_file_equals() { + let path = Utf8PathBuf::from("test.foo"); + + let mut db = TestDatabase::default(); + let ingot = InputIngot::core(&db); + + let file1 = InputFile::new(&db, path.clone(), ingot); + let file2 = InputFile::new(&db, path, ingot); + assert_eq!(file1, file2); + + file2.set_text(&mut db).to("Hello, world!".into()); + assert_eq!(file1.text(&db), file2.text(&db)); + } +} diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index b275ea94ea..9d224f990b 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -1,3 +1,4 @@ +mod core_ingot; pub mod diagnostics; pub mod indexmap; pub mod input; diff --git a/crates/driver/src/db.rs b/crates/driver/src/db.rs index c2054b0f69..e2fd472882 100644 --- a/crates/driver/src/db.rs +++ b/crates/driver/src/db.rs @@ -8,7 +8,7 @@ use common::{ diagnostics::CompleteDiagnostic, impl_db_traits, indexmap::IndexSet, - input::{IngotDependency, IngotKind, Version}, + input::{input_for_file_path, FilePath, IngotDependency, IngotKind, Version}, InputDb, InputFile, InputIngot, }; use hir::{ @@ -26,6 +26,9 @@ use hir_analysis::{ }, HirAnalysisDb, }; +use salsa::Setter; +// use include_dir::{include_dir, Dir}; +// use salsa::Setter; use crate::diagnostics::ToCsDiag; @@ -111,7 +114,9 @@ impl DriverDataBase { ); let file_name = root_file.file_name().unwrap(); - let input_file = InputFile::new(self, file_name.into(), source.to_string()); + let input_file = + input_for_file_path(self.as_input_db(), FilePath::from(self, file_name), ingot); + input_file.set_text(self).to(source.to_string()); ingot.set_root_file(self, input_file); ingot.set_files(self, [input_file].into_iter().collect()); (ingot, input_file) @@ -137,7 +142,8 @@ impl DriverDataBase { ); let file_name = root_file.file_name().unwrap(); - let input_file = InputFile::new(self, file_name.into(), source.to_string()); + let input_file = InputFile::new(self, file_name.into(), ingot); + input_file.set_text(self).to(source.to_string()); ingot.set_root_file(self, input_file); ingot.set_files(self, [input_file].into_iter().collect()); (ingot, input_file) @@ -193,7 +199,11 @@ impl DriverDataBase { ) -> IndexSet { let input_files = files .into_iter() - .map(|(path, content)| InputFile::new(self, path, content)) + .map(|(path, content)| { + let input_file = InputFile::new(self, path, ingot); + input_file.set_text(self).to(content); + input_file + }) .collect::>(); let root_file = *input_files diff --git a/crates/hir-analysis/tests/test_db.rs b/crates/hir-analysis/tests/test_db.rs index 97a0ebb6c7..408f57129c 100644 --- a/crates/hir-analysis/tests/test_db.rs +++ b/crates/hir-analysis/tests/test_db.rs @@ -36,6 +36,7 @@ use hir::{ HirDb, LowerHirDb, SpannedHirDb, }; use rustc_hash::FxHashMap; +use salsa::Setter; type CodeSpanFileId = usize; @@ -62,7 +63,8 @@ impl HirAnalysisTestDb { let kind = IngotKind::StandAlone; let version = Version::new(0, 0, 1); let ingot = InputIngot::new(self, file_name, kind, version, IndexSet::default()); - let root = InputFile::new(self, file_name.into(), text.to_string()); + let root = InputFile::new(self, file_name.into(), ingot); + root.set_text(self).to(text.to_string()); ingot.set_root_file(self, root); ingot.set_files(self, [root].into_iter().collect()); (ingot, root) diff --git a/crates/hir/src/hir_def/module_tree.rs b/crates/hir/src/hir_def/module_tree.rs index d1a3176440..665eade68a 100644 --- a/crates/hir/src/hir_def/module_tree.rs +++ b/crates/hir/src/hir_def/module_tree.rs @@ -295,13 +295,13 @@ mod tests { Version::new(0, 0, 1), Default::default(), ); - let local_root = InputFile::new(&db, "src/lib.fe".into(), "".into()); - let mod1 = InputFile::new(&db, "src/mod1.fe".into(), "".into()); - let mod2 = InputFile::new(&db, "src/mod2.fe".into(), "".into()); - let foo = InputFile::new(&db, "src/mod1/foo.fe".into(), "".into()); - let bar = InputFile::new(&db, "src/mod2/bar.fe".into(), "".into()); - let baz = InputFile::new(&db, "src/mod2/baz.fe".into(), "".into()); - let floating = InputFile::new(&db, "src/mod3/floating.fe".into(), "".into()); + let local_root = InputFile::new(&db, "src/lib.fe".into(), local_ingot); + let mod1 = InputFile::new(&db, "src/mod1.fe".into(), local_ingot); + let mod2 = InputFile::new(&db, "src/mod2.fe".into(), local_ingot); + let foo = InputFile::new(&db, "src/mod1/foo.fe".into(), local_ingot); + let bar = InputFile::new(&db, "src/mod2/bar.fe".into(), local_ingot); + let baz = InputFile::new(&db, "src/mod2/baz.fe".into(), local_ingot); + let floating = InputFile::new(&db, "src/mod3/floating.fe".into(), local_ingot); local_ingot.set_root_file(&mut db, local_root); local_ingot.set_files( &mut db, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d719f5f72d..c22c2e145e 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -46,6 +46,7 @@ mod test_db { InputDb, InputFile, InputIngot, }; use derive_more::TryIntoError; + use salsa::Setter; use super::HirDb; use crate::{ @@ -102,7 +103,8 @@ mod test_db { let kind = IngotKind::StandAlone; let version = Version::new(0, 0, 1); let ingot = InputIngot::new(self, path, kind, version, IndexSet::default()); - let file = InputFile::new(self, "test_file.fe".into(), text.to_string()); + let file = InputFile::new(self, "test_file.fe".into(), ingot); + file.set_text(self).to(text.into()); ingot.set_root_file(self, file); ingot.set_files(self, [file].into_iter().collect()); (ingot, file) diff --git a/crates/language-server/src/backend/workspace.rs b/crates/language-server/src/backend/workspace.rs index a818ae909f..7ea7befa04 100644 --- a/crates/language-server/src/backend/workspace.rs +++ b/crates/language-server/src/backend/workspace.rs @@ -110,7 +110,7 @@ impl IngotFileContext for LocalIngotContext { .files .get(path) .copied() - .unwrap_or_else(|| InputFile::new(db, path.into(), String::new())); + .unwrap_or_else(|| InputFile::new(db, path.into(), ingot)); self.files.insert(path, input); ingot.set_files(db, self.files.values().copied().collect()); Some((ingot, input)) @@ -181,7 +181,7 @@ impl IngotFileContext for StandaloneIngotContext { .files .get(path) .copied() - .unwrap_or_else(|| InputFile::new(db, path.into(), String::new())); + .unwrap_or_else(|| InputFile::new(db, path.into(), ingot)); let mut files = IndexSet::new(); files.insert(input_file); ingot.set_files(db, files);